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;
};