关于c ++:为什么operator ++返回非const值?

Why does operator ++ return a non-const value?

我看过Scott Meyers写的C++第三版。

本书的第3项"尽可能使用const",指出如果我们想防止将rvalues意外分配给函数的返回值,则返回类型应为const

例如,iterator的增量函数:

1
2
3
const iterator iterator::operator++(int) {
    ...
}

这样就避免了一些事故的发生。

1
2
3
4
5
6
7
iterator it;

// error in the following, same as primitive pointer
// I wanted to compare iterators
if (it++ = iterator()) {
  ...
}

但是,gcc中的std::vector::iterator等迭代器不会返回const值。

1
2
vector<int> v;
v.begin()++ = v.begin();   // pass compiler check

有什么原因吗?


我很肯定这是因为它会破坏右值引用和任何类型的decltype。即使这些特征不在C++ 03中,它们也已经知道了。

更重要的是,我不认为任何标准函数返回常量值,它可能是直到标准发布之后才被考虑的。此外,常量值通常被认为是不正确的?.并非所有非常量成员函数的使用都是无效的,返回常量值会空白地阻止它们。

例如,

1
auto it = ++vec.begin();

是完全有效的,而且确实是有效的语义,如果不完全需要的话。考虑一下我提供方法链的类。

1
2
3
4
5
6
7
8
9
10
class ILikeMethodChains {
public:
    int i;
    ILikeMethodChains& SetSomeInt(int param) {
        i = param;
        return *this;
    }
};
ILikeMethodChains func() { ... }
ILikeMethodChains var = func().SetSomeInt(1);

是否应该仅仅因为有时我们可能调用一个没有意义的函数而不允许这样做?不,当然不是。或者"swaptimization"怎么样?

1
2
3
std::string func() { return"Hello World!"; }
std::string s;
func().swap(s);

如果func()生成了一个常量表达式,这将是非法的,但它是完全有效的,而且确实,假设std::string的实现没有在默认构造函数中分配任何内存,既快速又清晰/可读。

你应该认识到,C++ 03的价值/价值规则坦率地说是没有道理的。事实上,它们只是部分烘焙,在允许一些可能的权利的同时,最起码要禁止一些公然的错误。C++ 0x rValy规则更加简洁和完善。


如果it是非常量,我希望*(++it)给予我对它所代表的事物的可变访问。

然而,取消对const迭代器的引用只会产生对它所代表的事物的不可变访问。.[strike>[编辑:否,这也是错误的。我真的放弃了!]

这是我能想到的唯一原因。

正如您正确地指出的,以下是不正确的,因为基元上的++生成一个右值(左值上不能有右值):

1
2
int* p = 0;
(p++)++;

所以这里的语言似乎有点不一致。


编辑:这并不能真正回答评论中指出的问题。我就把这封信留在这里,以防它有用…

我认为这在很大程度上是一个面向更好的可用接口的语法统一问题。当提供这样的成员函数而不区分名称,并且只让重载解析机制确定正确的版本时,您会阻止(或至少尝试)程序员使const相关的担忧。

我知道这似乎是矛盾的,特别是以你为例。但是,如果您考虑大多数用例,它是有意义的。采用类似于std::equal的STL算法。无论容器是否为常量,您总是可以编写类似于bool e = std::equal(c.begin(), c.end(), c2.begin())的代码,而无需考虑正确的开始和结束版本。

这是STL中的一般方法。记得operator[]…考虑到容器将与算法一起使用,这是合理的。尽管在某些情况下您可能仍然需要定义一个具有匹配版本(iteratorconst_iterator的迭代器,这一点也很明显。

好吧,这正是我现在想到的。我不确定这有多令人信服…

注意:使用常量迭代器的正确方法是通过const_iteratortypedef。