【问题标题】:Why GCC refuses a const reference within a copy-assignment operation?为什么 GCC 在复制赋值操作中拒绝 const 引用?
【发布时间】:2019-10-13 18:25:10
【问题描述】:

我想正常地重载一个常见的复制赋值运算符。 起初我使用了一个只需要对源代码进行 const 引用的接口, 并明确禁用接受可修改引用的接口, 但我无法通过编译。 编译器报“error: use of deleted function ‘ClassA& ClassA::operator=(ClassA&)”

当然,如果我不明确删除接口,我可以编译,但这不是我的目的。 我想明确删除它,以避免意外使用它。

为什么复制赋值操作需要对源的可修改引用,而不是 const 引用? 赋值操作只需要只读访问源即可!

关于复制构造函数有一个相同的问题,为了简化,我省略了它。

我的代码有什么问题?或者我们不能删除它?

我的示例代码如下:

class ClassA {
public:
   ClassA() = default;
   ClassA( ClassA & ) = default;

   ClassA & operator=( ClassA & )
   = delete   // Must comment-out this, or we can't pass the compilation.
   // { cout << "ClassA & operator=( ClassA & ) executed." << endl; return *this; }
   ;

   ClassA & operator=( ClassA && ) {
      cout << "ClassA & operator=( ClassA && ) executed." << endl;
      return *this;
   };

   ClassA & operator=( const ClassA & ) {
      cout << "ClassA & operator=( const ClassA & ) executed." << endl;
      return *this;
   };
   ClassA & operator=( const ClassA && ) {
      cout << "ClassA & operator=( const ClassA && ) executed." << endl;
      return *this;
   };
};

int main() {
   ClassA oa, ob;
   ob = oa;

   return EXIT_SUCCESS;
};

【问题讨论】:

  • 你为什么需要它?为什么改变自己会改变另一个实例?
  • 由于右值引用的性质,接受const 右值引用毫无意义。
  • 你需要下定决心。要么使用ClassA &amp;operator=(const ClassA &amp;) = delete,它会阻止编译器生成operator=(),这意味着类没有,或者你定义并实现运算符。这两个概念是相互排斥的。你不能指望显式删除一个操作符,然后显式实现它。

标签: c++ gcc const-reference copy-assignment move-assignment-operator


【解决方案1】:

或者我们不能删除它?

您只是不需要这样做。如果提供用户自定义的复制赋值运算符,则不会有其他的implicitly-declared,即只存在用户自定义的。

如果您这样做,您明确标记为delete 的复制赋值运算符将参与重载决议;当它被选中时,编译失败。对于ob = oa;operator=( ClassA &amp; ) 是更好的匹配,如果它不存在,operator=( const ClassA &amp; ) 将被使用并正常工作。

所以在这种情况下你可以这样做

class ClassA {
public:

   ClassA & operator=( ClassA && ) {
      cout << "ClassA & operator=( ClassA && ) executed." << endl;
      return *this;
   }

   ClassA & operator=( const ClassA & ) {
      cout << "ClassA & operator=( const ClassA & ) executed." << endl;
      return *this;
   }
};

【讨论】:

    【解决方案2】:

    当然,如果我不明确删除接口,我可以编译,但这不是我的目的。我想明确删除它,以避免意外使用它。

    您不能意外使用不存在的东西。如果您的类定义了ClassA &amp; operator=( const ClassA &amp; ),那么ClassA &amp; operator=( ClassA &amp; ) 将根本不存在(编译器不会生成它)。没有理由提供和删除它。

    【讨论】:

      【解决方案3】:

      如果您明确删除它,然后调用:

      ob = oa;
      // The same as
      ob.operator=(oa);
      

      当然ClassA &amp; operator=( ClassA &amp; ) 是最佳匹配,因为oa 是一个非常量左值。既然被删除了,就会报错。

      如果根本没有声明,ClassA &amp; operator=( const ClassA &amp; ) 现在成为最佳匹配。所以它永远不会尝试使用ClassA &amp; operator=( ClassA &amp; ),因为它不存在。

      如果你真的想要,你仍然可以拥有ClassA &amp; operator=( ClassA &amp; ) = delete;,并且你必须手动从 const 引用中分配:

      ob = static_cast<const ClassA&>(oa);
      // Will now select `ClassA & operator=( const ClassA & )`
      

      这表明您不需要非常量左值赋值运算符。但实际上没有意义,因为如果未声明 ClassA &amp; operator=( ClassA &amp; ),它将被用作 const 引用。

      【讨论】:

        猜你喜欢
        • 2017-03-15
        • 1970-01-01
        • 2012-02-06
        • 1970-01-01
        • 2021-12-22
        • 2016-04-08
        • 2022-12-16
        • 2019-03-11
        • 1970-01-01
        相关资源
        最近更新 更多