为什么 --string::end() 可以编译,而 --string.size() 不能编译?

Why does --string::end() compile but --string.size() does not?

本文关键字:编译 --string size 不能 end 为什么      更新时间:2023-10-16

代码

std::string str{ "foo" };
auto lastCharIndex{ --str.size() };

创建编译器错误lvalue required as decrement operand

auto lastCharIterator{ --str.end() };

不。这是为什么呢?

>string::size按值返回标量类型(某种整数),并且 prvalues 上不允许使用内置前缀 ---表达式。相比之下,对于重载运算符(在用户定义类型上),它们本质上只是函数,则不适用此类限制。因此,是否允许--str.end()取决于string::iterator是用户定义类型(某种类类型)还是标量类型(例如指针)。但是,这没有指定,甚至可能因优化设置而异。

我想补充一点,您可以自定义用户定义的成员函数,例如通过左值和右值引用调用时行为不同的++。例如,可以定义一个自定义iterator类,以防止在临时上调用修改成员函数。为此,需要使用引用限定符。

class iterator {
public:
iterator operator++() & { // for lvalues
std::cout << "incrementing...n";
}
iterator operator++() && = delete; // for rvalues
};

使用这些限定符,您仍然允许修改左值:

iterator it = ...;
++it; // totally fine

但是,您现在可以防止修改临时项,从而生成与内置类型(如size_t)更一致的用户定义类。

++(iterator{}); // ERROR

现场示例在这里

我不确定标准对std::string迭代器类型有什么看法,但原则上,你可以为自己的迭代器和其他类这样做,只要你认为修改临时总是一个错误。

基本类型算术类型的递减(和递增)运算符的操作数必须是左值。std::string::size返回整数类型的值(即不是引用),因此函数调用str.size()是一个 prvalue。prvalues 不是左值,因此--str.size()格式不正确。

类类型的递减(和递增)运算符重载的操作数可以在右值上调用,除非重载是左值引用限定的。如果--str.end()格式正确,那么我们可以推断迭代器是一个类类型(指针将是不是类类型的迭代器类型的一个例子),并且递减运算符重载确实没有左值引用限定符。据我所知,标准容器迭代器的成员函数是否是 ref 限定的,这是未指定的。

附言:同样适用于(复合)赋值运算符:赋值的left 手动操作数也必须是左值(除非它是具有非左值引用限定的重载的类类型)。事实上,这就是 l 值中">l"的起源。