在C++中学习迭代器时,我尝试了以下操作:
#include <vector>
int main() {
std::vector<int> a;
a.end()=a.begin();
//Why is this even allowed by the compiler?
}
我错过了什么?
例如,如果函数end返回指针,这是不可能的。
例如,此代码将不会编译
int a[] = { 1, 2, 3 };
std::end( a ) = std::begin( a );
GCC发布错误
error: lvalue required as left operand of assignment
std::end( a ) = std::begin( a );
^
但是,当使用类类型的对象时,它们可以调用ovetloaded复制(或移动)赋值运算符。
所以问题来了,为什么函数不返回常量迭代器对象。我认为应用operator ++
是可能的。例如
auto it = ++c.begin();
对于作为指针的直接访问迭代器,您可以简单地编写
auto it = begin( a ) + 1;
标准没有指定std::vector::iterator
是类类型还是原始指针。
如果它是一个类类型,则此代码对a.end()
返回的临时对象调用operator=
。这不是一个很有用的操作,但合法。(R值上可能调用了函数)。
如果您的库使std::vector::iterator
成为指针,那么此代码将无法编译,因为简单的赋值需要左侧的左值。
Jarod42指出,如果迭代器的赋值运算符被定义为:
std::vector::iterator& std::vector::iterator::operator =(std::vector::iterator) &;
那么该代码将是非法的;后面的CCD_ 6意味着该函数只有在左值上被调用时才是可选择的。
但它并不是这样定义的,我想委员会不想毫无理由地将一些法律法规定为非法;也许它有一个还没人想到的用例。
这行会影响临时迭代器,因此毫无用处。
允许std::vector::iterator = std::vector::iterator
。
禁止这种情况的一种方法是使用
std::vector::iterator::operator =(std::vector::iterator) &; // note the extra &
但std::vector::iterator
可能是一个简单的指针,我们不能否定T* = T*
不能在C++中为右值(也称为临时)基元类型赋值,如(int)7
或由函数值返回的指针。这很有道理,因为赋值的结果会立即被丢弃,所以这可能不是程序员想要的。rvalue在语言语法中的意思是"=
符号的右手边",或者是一个临时(匿名)值。
然而,对于类类型的对象,=
只调用operator=
。通常,当lhs是一个右值时,这是没有意义的,但有时它会(比如,类是一个代理对象伪引用(就像std::vector<bool>
上的[]
返回的一样)。因此,它被认为是非常特殊的,它只是调用该方法。并且无法判断该方法是从临时左值还是从"真实"左值调用的。
右值引用在C++11中是新的,右值限定方法也是新的。
在C++11之前,不能仅仅因为类类型的对象是临时对象就阻止对其调用operator=
。
由于begin
返回一个非常数临时,如果它是类类型(标准不保证),则可以将其分配给
今天,我们可以阻止对类类型的临时库的赋值,但标准库尚未被修改为在其类型上阻止它。可能至少部分是为了避免向后兼容性问题,部分是为了首先确定std
之外的最佳实践。
它们是允许的,因为迭代器对象是可复制分配的和可复制构造的。这就是为什么我们可以做一些类似a.begin()=a.end()的事情。此外,我们还可以做一些自动的事情(a.begin)。
查看下面关于可分配副本的示例。
#include <vector>
struct CopyConsAssignable
{
int x;
CopyConsAssignable(int a)
{
x = a;
}
CopyConsAssignable(const CopyConsAssignable& other)
{
x = other.x;
}
CopyConsAssignable& operator=(const CopyConsAssignable& other)
{
if (this != &other)
{
x = other.x;
}
return *this;
}
};
int main()
{
std::vector<int> a;
a.begin() = a.end();
CopyConsAssignable(2) = CopyConsAssignable(4); // Allowed as the objects are copy-assignable
return 0;
}