为什么GCC在复制分配操作中拒绝const引用?

时间:2019-05-28 09:03:58

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

我想正常地重载一个普通的副本分配运算符。 最初,我使用的接口只需要对源代码的const引用, 并明确禁用接受可修改引用的接口, 但我无法通过汇编。 编译器报告“错误:使用已删除的功能'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;
};

3 个答案:

答案 0 :(得分:4)

  

还是我们不能删除它?

您只是不需要这样做。如果您提供了一个用户定义的副本分配运算符,那么其他任何implicitly-declared都将是{= 3}。

如果这样做,您显式标记为delete的副本分配运算符将参与重载解析;选择后,编译将失败。对于ob = oa;operator=( ClassA & )是更好的匹配,如果它不存在,将使用operator=( const ClassA & )并且可以正常工作。

所以在这种情况下,您可以做

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;
   }
};

答案 1 :(得分:2)

  

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

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

答案 2 :(得分:1)

如果您明确删除它,然后致电:

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

当然,ClassA & operator=( ClassA & )是最佳匹配项,因为oa是非常量左值。由于已删除,因此将是错误的。

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

如果您确实愿意,您仍然可以拥有ClassA & operator=( ClassA & ) = delete;,并且必须从const引用中手动分配:

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

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