代码
std::string str{ "foo" };
auto lastCharIndex{ --str.size() };
创建编译器错误lvalue required as decrement operand
,但
auto lastCharIterator{ --str.end() };
没有。为什么会这样?
答案 0 :(得分:4)
string::size
按值返回标量类型(某种整数),并且prvalue上不允许内置前缀--
表达式。相比之下,对于重载运算符(基于用户定义的类型),它们本质上只是函数,没有这种限制。因此,是否允许--str.end()
取决于string::iterator
是用户定义类型(某种类类型)还是标量类型(例如指针)。不过,该参数未指定,甚至可能会根据优化设置而有所不同。
答案 1 :(得分:1)
基本类型算术类型的减量(和增量)运算符的操作数必须为左值。 std::string::size
返回整数类型的值(即不是引用),因此函数调用str.size()
是prvalue。 Prvalue不是左值,因此--str.size()
格式错误。
一个类类型的减量(和增量)运算符重载的操作数可以在一个右值上调用,除非该重载是左值ref限定的。如果--str.end()
的格式正确,则可以推断出迭代器是一个类类型(指针将是不是类类型的迭代器类型的一个示例),并且减量运算符重载的确没有左值ref限定词。据我所知,标准容器的迭代器的成员函数是否是ref限定的。
P.S。 (复合)赋值运算符也是如此:赋值的 l 右手操作数也必须是左值(除非它是带有重载的类类型,而该重载不是左值ref限定的)。实际上,这就是 l 值中“ l ”的来源。
答案 2 :(得分:1)
我想补充一下,您可以 自定义用户定义的成员函数,例如++
,这些成员函数在通过左值和右值引用调用时的行为会有所不同。例如,您可以定义一个自定义iterator
类,以防止您在临时实例上调用修改成员函数。为此,您需要使用reference qualifiers。
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
的迭代器类型对此标准有何规定,但是原则上,您可以对自己的迭代器和其他类执行此操作,无论您认为修改总是错误的地方临时的。