C++ 重载运算符[]而不获取;赋值的左操作数所需的左值;错误
这是所有“赋值左操作数所需的左值”错误问题的一个倒数。C++ 重载运算符[]而不获取;赋值的左操作数所需的左值;错误,c++,operator-overloading,lvalue,C++,Operator Overloading,Lvalue,这是所有“赋值左操作数所需的左值”错误问题的一个倒数。 我有一个重载运算符[]的类,但只有返回临时值的版本。如果要返回int: struct Foo { int operator[]( int idx ) const { return int( 0 ); } }; Foo f; f[1] = 5; 我将正确地得到左值编译器错误。但是,如果它返回结构类型,编译器(本例中为GCC 7.2)根本不会抱怨: struct Bar {}; struct Foo { Bar operat
我有一个重载运算符[]的类,但只有返回临时值的版本。如果要返回int:
struct Foo
{
int operator[]( int idx ) const { return int( 0 ); }
};
Foo f;
f[1] = 5;
我将正确地得到左值编译器错误。但是,如果它返回结构类型,编译器(本例中为GCC 7.2)根本不会抱怨:
struct Bar {};
struct Foo
{
Bar operator[]( int idx ) const { return Bar(); }
};
Foo f;
f[1] = Bar();
如果酒吧是临时的,而且没有专门的操作员,为什么不以同样的方式投诉呢?
另一个问题,有什么办法可以让你抱怨吗?如果这样使用,显然这是一个编码错误
有什么办法可以让你抱怨吗
可以将显式默认赋值运算符与ref限定符一起使用:
struct Bar {
Bar& operator=(const Bar&) & = default;
// ^
这使得右值的赋值形式错误,而左值的赋值形式正确
请注意,声明赋值运算符将禁用隐式移动赋值,因此如果需要,您可能还需要定义它(也作为默认值,如果合适,还可能使用右值引用限定符)
如果酒吧是临时的,而且没有专门的操作员,为什么不以同样的方式投诉呢
因为隐式生成的赋值运算符不是ref限定的
如果这样使用,显然这是一个编码错误
赋值为右值并不是一个普遍的错误。对于某些行为类似于引用的类型,右值的赋值是自然的。这是因为赋值修改的是引用的对象,而不是临时对象本身
典型的用例是分配给右值std::tie
(示例来自):
std::set of_s;//S比不上可比性
S值{42,“测试”,3.14};
std::set::迭代器iter;
布尔插入;
//将insert的返回值解压缩到iter并插入
标准::tie(iter,插入)=插入值的集合;
是的,如果隐式运算符是限定的,而非限定的则需要显式声明,这可能会更好,因为引用类型是例外的,而不是规范。但这不是语言的现状,更改它是一种向后不兼容的更改。是的,有一种方法可以通过删除以下方法将其变成编译错误:
Bar& operator=(const Bar&)&& =delete;
Bar& operator=(Bar&&)&& =delete;
请注意,这将禁用其他运算符和构造函数的自动生成,所以您必须全部定义它们:
struct Bar {
Bar()=default;
Bar(const Bar&) = default;
Bar& operator=(const Bar&)&& =delete;
Bar& operator=(Bar&&)&& =delete;
Bar& operator=(const Bar&)& =default;
Bar& operator=(Bar&&)& =default;
};
测试用例折叠为:
Bar()=Bar()代码>为我编译!我希望至少有一个实时警告:不确定这是否是一个选项,但如果您将Bar
的赋值运算符设置为private,它将不会编译。另请参见,该结构可能有一个默认赋值运算符,允许您在执行对象[index]=object()
时进行赋值?这对我来说似乎相当明显。。所以我测试了一下。。结果表明,如果删除赋值运算符,它会抱怨。。对我来说似乎得到了证实。是的,将赋值运算符设置为私有将防止出现这种情况,但它也会阻止我在确实希望能够赋值时进行赋值。我要补充的是,如果Bar
具有移动操作与复制操作不同的成员,“你要遵守五的规则,因为这个声明必然违反零的规则。”阿斯切普勒补充道。有趣的是,这是第五条规则的一个明显例外,因为您根本不需要接触析构函数。复制赋值运算符还意味着没有隐式声明的移动构造函数(并且不赞成使用隐式声明的复制构造函数)。如果您有默认值
一个,其他的不是隐式删除的吗,因此,您不需要删除
行?@MooingDuck-Yes,但它也会删除移动赋值和移动构造函数。此外,这清楚地表达了意图。仅设置&=默认值代码>一直工作,直到有人决定不需要&
,或者应该添加&
。我要说的是,这些资格赛并不是很有名,因为它们很少见。是的,您可以并且应该添加注释,但是您也可以使用=delete
用代码表示注释。
struct Bar {
Bar()=default;
Bar(const Bar&) = default;
Bar& operator=(const Bar&)&& =delete;
Bar& operator=(Bar&&)&& =delete;
Bar& operator=(const Bar&)& =default;
Bar& operator=(Bar&&)& =default;
};