【问题标题】:Copy assignment operator defined in template being deleted by compiler模板中定义的复制赋值运算符被编译器删除
【发布时间】:2014-10-04 11:25:30
【问题描述】:

我熟悉的原理(例如,来自this answerthis one)当一个类有一个移动构造函数和/或移动赋值运算符时,它的默认复制构造函数和复制赋值运算符被删除。但是,在我看到的示例中,这可以通过显式定义新的复制构造函数和赋值运算符来解决。

在我的特定情况下,我有一个类,它是通过从 C 样式结构和模板类的联合继承派生的。复制和移动赋值运算符在模板中显式定义,而复制和移动构造函数在类本身中显式定义。换句话说,一切都是明确定义的,虽然不是都在同一个地方。下面是一些示例代码:

typedef struct {
    int n;
} myStruct;

template <typename T> class myTemplate
{
public:
    // Default constructor
    myTemplate<T>() : t_n(nullptr) {}

    // Cannot create copy or move constructors in template, as cannot 
    // access the 'n' member directly

    // Copy assignment operator
    myTemplate<T> & operator=(const myTemplate<T> &source)
    {
        if (this != &source)
        {
            *t_n = *(source.t_n);
        }
        return *this;
    }

    //! Move assignment operator
    myTemplate<T> & operator=(myTemplate<T> &&source)
    {
        if (this != &source)
        {
            *t_n = *(source.t_n);
            *(source.t_n) = 0;
            source.t_n = nullptr;
        }
        return *this;
    }

    T* t_n;
};

class myClass : public myStruct, public myTemplate<int>
{
public:
    // Default constructor
    myClass() : myTemplate<int>()
    {
        n = 0;
        t_n = &n;
    }

    // Alternative constructor
    myClass(const int &n_init) : myTemplate<int>()
    {
        n = n_init;
        t_n = &n;
    }

    // Copy constructor
    myClass(const myClass &source) : myTemplate<int>()
    {
        n = source.n;
        t_n = &n;
    }

    // Move constructor
    myClass(myClass &&source) : myTemplate<int>()
    {
        n = source.n;
        t_n = &n;
        source.n = 0;
        source.t_n = nullptr;
    }
};

int main()
{

    myClass myObject(5);
    myClass myOtherObject;

    // Compilation error here:
    myOtherObject = myObject;

    return 1;
}

在 Windows 上的 Visual C++ 和 Intel C++ 中,这完全符合我的预期。然而,在 Linux 中的 gcc 4.9.0 上,我收到了可怕的错误消息:

g++ -c -std=c++11 Main.cppMain.cpp: In function ‘int main()’:
Main.cpp:78:19: error: use of deleted function ‘myClass& myClass::operator=(const myClass&)’
     myOtherObject = myObject;
               ^
Main.cpp:39:7: note: ‘myClass& myClass::operator=(const myClass&)’ is implicitly declared as deleted because ‘myClass’ declares a move constructor or move assignment operator
 class myClass : public myStruct, public myTemplate<int>

果然,如果我在类本身而不是在模板中定义显式复制赋值运算符,错误就会消失,但这很麻烦,并且破坏了使用模板的优势,因为 (a) 我的实际副本赋值运算符比这里显示的要大得多,并且 (b) 有大量不同的类都共享这个模板。

那么,这仅仅是 gcc 4.9.0 中的一个错误,还是实际上标准所说的应该发生?

【问题讨论】:

  • 只需明确默认myClass 中的赋值运算符即可。这不是负担。

标签: c++ c++11 multiple-inheritance move-semantics


【解决方案1】:

GCC 是正确的(Clang 和 EDG 同意)。

myTemplate 有一个用户声明的移动构造函数,因此它的复制赋值运算符被删除。

您提供了一个复制构造函数,但没有提供复制赋值运算符。只需为myTemplate 声明一个复制赋值运算符并将其定义为默认值。这需要多一行代码。

【讨论】:

  • 这就是答案(@Howard Hinnant 在两分钟前的评论中也给出了答案)。我今天学习了一些新的 C++,因为我以前从未听说过“默认操作符”。我添加了两行“myClass & operator=(const myClass &source) = default;”和“myClass & operator=(myClass &&source) = default;”进入我的班级定义,一切都按预期工作。不过,我仍然认为这很奇怪。
  • @EosPengwern:现在是坏消息。 Visual C++ 尚未实现默认移动成员...
  • @Howard Hinnant;太对了,尽管英特尔 C++ 编译器没有抱怨。我需要在第二个声明周围加上#IFNDEF _MSC_VER,突然间,我精心设计的模板代码看起来就像狗的早餐!
猜你喜欢
  • 1970-01-01
  • 2020-02-28
  • 1970-01-01
  • 2016-05-18
  • 1970-01-01
  • 1970-01-01
  • 2014-05-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多