我不记得是哪个话题,但是最近我看了CppCon 2017的一些谈话,有人在旁注中提到,重载operator=的唯一真正方法是采用以下方式:

class test {
public:
    test& operator=(const test&) &;
};

他明确强调了结尾的&,但没有说明它的作用。

那怎么办呢?

最佳答案

引用限定词-在C++ 11中引入

引用限定符不是C++ 17的功能(查看问题的标签),而是C++ 11中引入的功能。

struct Foo
{
  void bar() const &  { std::cout << "const lvalue Foo\n"; }
  void bar()       &  { std::cout << "lvalue Foo\n"; }
  void bar() const && { std::cout << "const rvalue Foo\n"; }
  void bar()       && { std::cout << "rvalue Foo\n"; }
};

const Foo&& getFoo() { return std::move(Foo()); }

int main()
{
  const Foo c_foo;
  Foo foo;

  c_foo.bar();            // const lvalue Foo
  foo.bar();              // lvalue Foo
  getFoo().bar();         // [prvalue] const rvalue Foo
  Foo().bar();            // [prvalue] rvalue Foo

  // xvalues bind to rvalue references, and overload resolution
  // favours selecting the rvalue ref-qualifier overloads.
  std::move(c_foo).bar(); // [xvalue] const rvalue Foo
  std::move(foo).bar();   // [xvalue] rvalue Foo
}

请注意,右值可用于初始化const左值引用(并以此扩展由右值标识的对象的生存期),这意味着,如果我们从上例中删除右值ref-qualifier重载,则右值值类别在示例中,将全部支持剩余的const &重载:
struct Foo
{
  void bar() const & { std::cout << "const lvalue Foo\n"; }
  void bar()       & { std::cout << "lvalue Foo\n"; }
};

const Foo&& getFoo() { return std::move(Foo()); }

int main()
{
  const Foo c_foo;
  Foo foo;

  // For all rvalue value categories overload resolution
  // now selects the 'const &' overload, as an rvalue may
  // be used to initialize a const lvalue reference.
  c_foo.bar();            // const lvalue Foo
  foo.bar();              // lvalue Foo
  getFoo().bar();         // const lvalue Foo
  Foo().bar();            // const lvalue Foo
  std::move(c_foo).bar(); // const lvalue Foo
  std::move(foo).bar();   // const lvalue Foo
}

参见例如以下博客文章作了简要介绍:
  • Andrzej's C++ blog - Ref-qualifiers


  • 右值不能调用非const &重载

    为了可能解释您从CppCon演讲中收集到的报价的意图,



    我们访问[over.match.funcs]/1, /4 & /5 [强调我的]:



    从上面的/5开始,以下重载(其中显式的& ref-qualifier被省略)
    struct test
    {
        test& operator=(const test&) { return *this }
    }
    

    允许将值分配给r值,例如
    int main()
    {
        test t1;
        t1 = test(); // assign to l-value
        test() = t1; // assign to r-value
    }
    

    但是,如果我们使用& ref-qualifier显式声明重载,则[over.match.funcs]/5.1不适用,并且只要我们不提供使用&& ref-qualifier声明的重载,就不会允许r值赋值。
    struct test
    {
        test& operator=(const test&) & { return *this; }
    };
    
    int main()
    {
        test t1;
        t1 = test(); // assign to l-value
        test() = t1; // error [clang]: error: no viable overloaded '='
    }
    

    对于声明自定义赋值运算符重载时是否明确包括& ref限定符,我是否会发表任何意见,但我敢于推测,然后我猜想,这样做的意图是什么?声明不包括“按值分配”。

    作为一个经过适当设计的赋值运算符,可以说永远不要是operator=(const不会有太大意义),并且由于不能使用rvalue初始化非常量左值引用,因此给定类型const T& operator=(const T&) const &operator=重载仅包含T的对象将永远不会避免可行的重载,该重载可以从标识为右值类别的T& operator=(const T&) &对象中调用。

    09-05 00:15
    查看更多