为啥定义复制构造函数会给我错误:不能将'obj&'类型的非常量左值引用绑定到'obj'类型的右值?

Posted

技术标签:

【中文标题】为啥定义复制构造函数会给我错误:不能将\'obj&\'类型的非常量左值引用绑定到\'obj\'类型的右值?【英文标题】:Why defining copy constructor gives me error: cannot bind non-const lvalue reference of type 'obj&' to an rvalue of type 'obj'?为什么定义复制构造函数会给我错误:不能将'obj&'类型的非常量左值引用绑定到'obj'类型的右值? 【发布时间】:2019-12-28 17:57:14 【问题描述】:
#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' 类型的右值

为什么定义单参数复制构造函数会产生这样的错误?请注意,在双参数复制构造函数的情况下,根本没有错误。

【问题讨论】:

复制构造函数具有这种签名:Foo(Foo const&amp;)Foo(Foo&amp;) 不是一个好的复制构造函数。 我会记住这一点。但是,这意味着我有一个半复制构造函数,因为我不能放弃在其中定义引用 &amp;A&amp;B。问题依旧。 @Eljay -- 根据标准,复制构造函数采用Foo&amp;const Foo&amp;volatile Foo&amp;const volatile Foo&amp; 类型的参数。所以Foo(Foo&amp;) 是一个非常好的复制构造函数。 错误消息几乎肯定比这里引用的要长。它应该通过导致失败的各种调用进行回溯。这会告诉你问题是从代码的哪里开始的。 顺便说一句,没有“双参数复制构造函数”之类的东西。复制构造函数只接受一个参数,或者,如果它接受多个参数,则除第一个参数之外的所有参数都必须具有默认值。复制构造函数复制一个对象 【参考方案1】:

典型的复制构造函数采用const T&amp;,在本例中为const Assembly&amp;。 由于您没有这个,标准库容器(例如std::vector)无法复制您的类型,如果它需要重新分配数组(例如使其更大),则需要这样做。

但是,std::vector&lt;T&gt; 有一个 noexcept 移动构造函数的std::vector&lt;T&gt; 将改为使用移动构造函数,从而避免没有复制构造函数的问题。

因此,以下添加允许它工作:

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

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

作为旁注,我建议将它们重命名为 AsmAAsmB,以防止窃笑。

编辑:

从根本上讲,问题在于单个参数Assembly&amp; 构造函数,它导致复制构造函数没有被隐式生成。由于不直观,与复制构造函数的相似性也很差。 Assembly thing = other; 实际上会调用奇怪的 Assembly&amp;constructor,而您希望它只是复制。因此,解决问题的最佳方法就是摆脱它。

选项 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);

【讨论】:

我听从了你的建议,在上述构造函数的定义上方添加了这一行,它与上面的 MRE 配合得很好,但在我的主要代码中,我得到了:error: use of deleted function 'Assembly::Assembly(Assembly&amp;&amp;)'info: 'Assembly::Assembly(Assembly&amp;&amp;) noexcept' is implicitly deleted because its exception-specification does not match the implicit exception-specification' 一旦我删除了最后一个部分,即使用Assembly(Assembly&amp;&amp;) noexcept,它甚至可以在那里工作。 您的见解非常有帮助!不幸的是,这并没有解决另一个错误(从一开始也存在):error: binding reference of type 'Assembly&amp;' to 'const Assembly' discards qualifiers。我相信我的 stl 实现根本不支持这种方法,但是,我在想,我是否应该重新提出问题来解决这个问题。

以上是关于为啥定义复制构造函数会给我错误:不能将'obj&'类型的非常量左值引用绑定到'obj'类型的右值?的主要内容,如果未能解决你的问题,请参考以下文章

当我在 ngOnInit() 中使用 router.getCurrentNavigation() 时,它会给我类型错误,但是当我在构造函数中使用它时,它工作正常,为啥?

为啥这个 fgets 函数会给我一个分段错误?

为啥在将唯一指针插入无序映射时 C++ 会给我一个错误?

为啥这会给我一个空引用异常? [复制]

为啥标准不将模板构造函数视为复制构造函数?

为啥隐式复制构造函数调用基类复制构造函数而定义的复制构造函数不调用?