提问人:cadam 提问时间:6/22/2023 更新时间:6/23/2023 访问量:141
“*this = {}”在成员函数中是否有效,以便在 CPP 中重置
Is "*this = {}" valid inside a member function for reset in CPP
问:
最近,我不高兴地发现,每次我必须为对象创建“重置”函数时,我经常会重现构造函数的行为。
例:
class Foo
{
int i;
public:
Foo() : i(42) {};
void reset() {
i = 42;
}
};
为了解决这个问题,我想出了直接在 reset 函数中调用构造函数的想法,以确保行为相同。
修改后的示例:
class Foo
{
int i;
public:
Foo() : i(42) {};
void reset() {
*this = {};
}
};
这很有效,但我想到了几个问题,我还没有找到答案:
- 这样做有效吗?这是UB吗?
- 鉴于它涉及创建和复制对象,以这种方式(不进行优化)会不会效率较低?
- 您认为这是避免重复的好做法吗?还是有另一种更合意的处理方式?
谢谢
答:
是的,没关系。
您正在做的是使用隐式定义的 operator=(由编译器免费提供,因为您自己没有定义一个)。
operator= 需要对另一个 Foo 对象的引用:
operator=(const Foo&)
或
operator=(Foo&&)
你给它一个空列表初始化,它是为你创建的 Foo 完美定义的,因为未标记为显式的类构造函数是从给定的参数列表到由匹配构造函数构造的类进行可能的隐式转换的定义。
在你的例子中,由于你给出了一个空列表,你只需要求隐式转换为 Foo{},这意味着你希望有这个代码:
*this = Foo{};
这意味着为我的对象分配默认构造的 Foo,它与 i=42 一起诞生。
默认 operator= 只需将作为参数给出的对象按位复制到执行对象 (*this) 中。
所以在此之后,你得到 this->i = 42。
它是完美定义的,但如果没有优化,它的效率不如你的第一个版本。但是,这是一个过早的优化,您需要确定它没有优化,然后才能选择更复杂的代码来理解。
您的第一个编码版本具有代码重复。在第二个版本中,你清楚地告诉其他程序员,这是一个默认的 Foo,然后你重置为一个。
在此处了解在不进行优化的情况下,您的第一个版本如何更高效,并且需要更少的组装说明。但是,即使使用最小的优化 -O1,它也在进行优化。因此,除非相关基准另有证明,否则我不会担心它。
如果证明代码效率低下,则可以使用最少的代码重复和更安全(不易出错)的编码模式来执行此操作:
class Foo
{
int m1 = m1_reset();
int m2 = m2_reset();
constexpr void internal_reset() {
m1 = m1_reset();
m2 = m2_reset();
}
constexpr int m1_reset() { return 42; }
constexpr int m2_reset() { return 84; }
public:
constexpr Foo() = default;
void reset() {
internal_reset();
}
};
- 在 C++20 中,使用 consteval 而不是 consexpr,尽管它不应该对生成的机器代码产生影响。
评论
是的,这是完全合法的。
在某些情况下可能不起作用(例如,如果默认构造函数是 ,也许在其他一些情况下),但将始终有效。= {}
explicit
= Foo{}
鉴于它涉及创建和复制对象,以这种方式(不进行优化)会不会效率较低?
也许它的效率略低,但不要担心过早的优化,除非你绝对确定这将是 yoru 代码中对性能至关重要的部分。
您认为这是避免重复的好做法吗?还是有另一种更合意的处理方式?
是的,避免重复是一件好事。我不知道有更好的方法。
评论
reset
val = {};
reset
my_variable = {};
my_variable.reset();