为什么编译器在C++中允许vector.begin()=vector.end()

Why does the compiler allow vector.begin()=vector.end() in C++?

本文关键字:vector begin end 编译器 C++ 为什么      更新时间:2023-10-16

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;
}