为啥定义复制构造函数会给我错误:不能将'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&)
。 Foo(Foo&)
不是一个好的复制构造函数。
我会记住这一点。但是,这意味着我有一个半复制构造函数,因为我不能放弃在其中定义引用 &A
和 &B
。问题依旧。
@Eljay -- 根据标准,复制构造函数采用Foo&
、const Foo&
、volatile Foo&
或const volatile Foo&
类型的参数。所以Foo(Foo&)
是一个非常好的复制构造函数。
错误消息几乎肯定比这里引用的要长。它应该通过导致失败的各种调用进行回溯。这会告诉你问题是从代码的哪里开始的。
顺便说一句,没有“双参数复制构造函数”之类的东西。复制构造函数只接受一个参数,或者,如果它接受多个参数,则除第一个参数之外的所有参数都必须具有默认值。复制构造函数复制一个对象。
【参考方案1】:
典型的复制构造函数采用const T&
,在本例中为const Assembly&
。
由于您没有这个,标准库容器(例如std::vector
)无法复制您的类型,如果它需要重新分配数组(例如使其更大),则需要这样做。
但是,std::vector<T>
有一个 noexcept 移动构造函数的std::vector<T>
将改为使用移动构造函数,从而避免没有复制构造函数的问题。
因此,以下添加允许它工作:
Assembly(Assembly&&) noexcept = default; // generate a noexcept move constructor
请注意,这将导致新构造的对象也指向相同的 AssA
和 AssB
对象。
作为旁注,我建议将它们重命名为 AsmA
和 AsmB
,以防止窃笑。
编辑:
从根本上讲,问题在于单个参数Assembly&
构造函数,它导致复制构造函数没有被隐式生成。由于不直观,与复制构造函数的相似性也很差。 Assembly thing = other;
实际上会调用奇怪的 Assembly&
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&&)'
,info: 'Assembly::Assembly(Assembly&&) noexcept' is implicitly deleted because its exception-specification does not match the implicit exception-specification'
一旦我删除了最后一个部分,即使用Assembly(Assembly&&) noexcept
,它甚至可以在那里工作。
您的见解非常有帮助!不幸的是,这并没有解决另一个错误(从一开始也存在):error: binding reference of type 'Assembly&' to 'const Assembly' discards qualifiers
。我相信我的 stl 实现根本不支持这种方法,但是,我在想,我是否应该重新提出问题来解决这个问题。以上是关于为啥定义复制构造函数会给我错误:不能将'obj&'类型的非常量左值引用绑定到'obj'类型的右值?的主要内容,如果未能解决你的问题,请参考以下文章
当我在 ngOnInit() 中使用 router.getCurrentNavigation() 时,它会给我类型错误,但是当我在构造函数中使用它时,它工作正常,为啥?