为什么定义复制构造函数给我一个错误:无法将类型'obj&'的非常量左值引用绑定到类型'obj'的右值?

时间:2019-12-28 17:57:14

标签: c++ oop c++11

#include <iostream>
#include <vector>
#include <array>
#define Nbodies 3

class Assembly {
public:
    // initializing constructor
    Assembly(double _dataA)
        : data(_dataA), AssA(nullptr), AssB(nullptr) { }

    // double argument copy constructor
    Assembly(Assembly &A, Assembly&B)
        : AssA(&A), AssB(&B) {
        data = A.data * B.data;
    }

    // single argument copy constructor - generates errors once uncommented
/*
    Assembly(Assembly &A)
        : data(A.data), AssA(&A), AssB(&A) {
        // initialize other members of this class here
    }
*/
    double data;
private:
    // these are const pointers to non-const objects of a type Assembly
    Assembly *const AssA, *const AssB;
};

int main() {
    std::array<double, Nbodies> datas = {1.0, 2.0, 3.0};

    // Initilize first branch of the binary tree
    std::vector<Assembly> base_assembly;
    for (int i = 0; i < Nbodies; i++) {
        base_assembly.emplace_back(datas[i]);
    }

    // Binary tree assembly - MWE (for general case, I'm using nested for loop)
    Assembly AssemblyAB = Assembly(base_assembly[0], base_assembly[1]);
    Assembly AssemblyC = Assembly(base_assembly[2]);
    Assembly AssemblyS = Assembly(AssemblyAB, AssemblyC);

    std::cout << AssemblyS.data << std::endl;

    return 0;
}

我正在研究一个程序,该程序递归地生成二进制尝试。当我有一个带有奇数个元素的分支时,我需要将一个元素“重写”到一个较低的分支。为此,我使用了一个拷贝构造函数,因为我需要初始化该类的其他成员(我使用的是Eigen库,并且某些操作不能作为初始化列表中的单行代码完成)。

当我定义一个单参数复制构造函数时,我的问题出现了。我收到以下错误:

  

... / mingw64 / lib / gcc / x86_64-w64-mingw32 / 8.1.0 / include / c ++ / bits / stl_construct.h:75:7:   错误:

     

无法将“ Assembly&”类型的非常量左值引用绑定到   类型为“ Assembly”的右值

为什么定义单个参数副本构造函数会产生这样的错误?请注意,对于具有两个参数的副本构造函数,根本没有错误。

1 个答案:

答案 0 :(得分:1)

典型的复制构造函数采用const T&,或者在这种情况下为const Assembly&。 由于您没有该类型,因此无法使用std::vector之类的标准库容器来复制您的类型,如果需要重新分配数组(例如使其变大),则需要这样做。

但是,std::vector<T>(其中T具有noexcept移动构造函数)将改用move构造函数,从而避免了没有复制构造函数的问题。

因此,以下新增功能可以使其工作:

Assembly(Assembly&&) noexcept = default; // generate a noexcept move constructor

请注意,这将导致新构造的对象也指向相同的AssAAssB对象。

作为旁注,我建议将它们重命名为AsmAAsmB(如果只是为了防止窃笑)。

编辑:

从根本上讲,问题在于单个参数Assembly&的构造函数,这导致不会隐式生成副本构造函数。由于不直观,与副本构造函数的相似性也很差。当您希望Assembly thing = other;仅复制它时,实际上会调用Assembly&这个奇怪的构造函数。因此,解决该问题的最佳答案就是摆脱它。

选项1:当您想要这种行为时,只需使用2参数构造函数并将同一对象传递两次即可。

选项2:使其成为标签构造函数:

// the tag type we'll use to select the weird constructor
inline struct same_asm_t {} same_asm; // not sure what version of C++ you're on; if this doesn't work remove inline and define in a cpp file elsewhere

// define this as a tag constructor (take some other "tag" type as a param for overload resolution)
Assembly(Assembly &A, same_asm_t) : data(A.data), AssA(&A), AssB(&A) {}

// use it like this, where other is an instance of Assembly
Assembly thing(other, same_asm);