重载operator []并没有收到“需要左值作为赋值的左操作数”错误

时间:2019-06-17 22:01:55

标签: c++ operator-overloading lvalue

这是所有“分配左操作数所需的左值”错误问题的反函数。
我有一个重载operator []的类,但是只有返回临时类的版本。如果要返回整数:

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();

如果Bar是临时的并且没有专门的运算符=,为什么不会以同样的方式抱怨呢? 另一个问题,有什么办法可以使这个抱怨?显然,如果使用这种方式,这是一个编码错误

2 个答案:

答案 0 :(得分:16)

  

有什么办法可以使这个抱怨?

您可以将显式默认的赋值运算符与ref限定符一起使用:

struct Bar {
    Bar& operator=(const Bar&) & = default;
//                             ^

这使得右值的赋值格式不正确,而左值的赋值格式仍然正确。

请注意,声明赋值运算符将禁用隐式移动赋值,因此,如果需要,您可能还需要对其进行定义(也是默认设置,如果合适,还可以使用rvalue ref限定符)。

  

如果Bar是临时的并且没有专门的运算符=,为什么不会以同样的方式抱怨呢?

因为隐式生成的赋值运算符没有ref限定。

  

如果以这种方式使用,显然这是一个编码错误

分配右值通常不是错误。对于某些应该表现为引用形式的类型,右值的分配是很自然的。之所以如此,是因为该分配修改了引用的对象,而不是临时对象本身。

一个典型的用例是分配一个右值std::tie(来自cppreference的示例):

std::set<S> set_of_s; // S is LessThanComparable

S value{42, "Test", 3.14};
std::set<S>::iterator iter;
bool inserted;

// unpacks the return value of insert into iter and inserted
std::tie(iter, inserted) = set_of_s.insert(value);

是的,考虑到引用类型是例外而不是规范,如果隐式运算符是合格的,而对于不合格的则需要显式声明可能会更好。但这不是语言的方式,对其进行更改是向后不兼容的更改。

答案 1 :(得分:1)

是的,有一种方法可以通过删除以下方法将其变成编译错误:

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