c++ 在CPP中重置的成员函数中“*this = {}”是否有效

h9vpoimq  于 2023-06-25  发布在  其他
关注(0)|答案(2)|浏览(75)

最近,我很不高兴地发现,每次我不得不为一个对象创建一个“重置”函数时,我都要重复构造函数的行为。
示例:

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吗?
  • 考虑到它涉及到创建和复制一个对象,这样做(没有优化)会不会效率更低?
  • 你认为这是避免重复的好做法吗?还是有其他更符合双方同意的方式?

谢谢

hc8w905p

hc8w905p1#

是的,很好。
你所做的是使用隐式定义的运算符=(编译器免费提供,因为你自己没有定义)。
operator=期望引用另一个Foo对象:

operator=(const Foo&)

operator=(Foo&&)

你给它一个空的list initializing,它是为你创建的Foo定义的,因为一个没有标记为显式的类构造函数是一个定义,用于从给定的参数列表到由匹配构造函数构造的类的可能的隐式转换。
在你的例子中,由于你给出的是一个空列表,你只需要隐式转换为Foo{},这意味着你希望得到以下代码:

*this = Foo{};

这意味着用默认构造的Foo来分配我的对象,它生来就是i=42。
默认操作符=将简单地将作为参数给出的对象按位复制到执行对象(*this)中。
所以在这之后,你得到这个->i = 42。
它是完美的定义,但没有优化,它比你的第一个版本效率低。但是,这是一个过早的优化,您需要在选择更复杂的代码之前确定它没有优化。
你的第一个编码版本有代码重复。在第二个版本中,您与其他程序员清楚地沟通,这是一个默认的Foo,您可以重置为一个。
在此了解在没有优化的情况下,您的第一个版本如何更高效,并且需要更少的汇编指令。然而,即使是最小的优化,例如O 1,它也在被优化。所以我不会担心,除非有相关的基准证明。
如果证明代码效率低下,你可以用最少的代码重复和更安全的编码模式(更少的bug)来做到这一点:

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,尽管它不会对生成的机器代码产生影响。
hlswsv35

hlswsv352#

是的,这完全法律的。
在某些情况下,= {}可能不起作用(例如:如果默认的构造函数是explicit,可能在其他一些情况下),但= Foo{}将始终工作。
考虑到它涉及到创建和复制一个对象,这样做(没有优化)会不会效率更低?
也许它的效率稍低,但是不要担心过早的优化,除非你绝对确定这将是你代码中性能关键的部分。
你认为这是避免重复的好做法吗?还是有其他更符合双方同意的方式?
是的,避免重复是一件好事。我不知道还有什么更好的办法。

相关问题