为什么--string :: end()编译但--string.size()不编译?

时间:2019-04-24 00:21:25

标签: c++ decrement

代码

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

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

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

没有。为什么会这样?

3 个答案:

答案 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

Live example here

我不确定std::string的迭代器类型对此标准有何规定,但是原则上,您可以对自己的迭代器和其他类执行此操作,无论您认为修改总是错误的地方临时的。