为什么是非法的:将指针向量复制到指向常量的指针向量中

Why is it illegal : copying vector of pointers into a vector of pointers to constants

本文关键字:向量 指针 常量 复制 为什么 非法      更新时间:2023-10-16

问题

以下代码不会在 C++11(也不是 C++14)中编译。我理解编译器的错误输出,但为什么标准不允许呢?

//main.cpp
#include <vector>
int main(void)
{
    double a = 3.0;
    double b = 3.0;
    //It works with mere pointers
    const double* ptrToConst = &a;
    /***/ double* ptrToObj   = &a;
//  ptrToObj = ptrToConst; //Illegal : that's understandable…
    ptrToConst = ptrToObj;   //Works
    //But the same doesn't work with vectors to pointers
    std::vector<const double*> ptrsToConst = {&a, &b};
    std::vector</***/ double*> ptrsToObj   = {&a, &b};
//  ptrsToObj = ptrsToConst; //Illegal : that's understandable
    ptrsToConst = ptrsToObj; //Illegal : but why?!
}

错误来自行ptrsToConst = ptrsToObj。实际上,似乎不可能将指针向量std::vector<T*>复制到指向常量的指针向量std::vector<const T*>。请注意,在这两种情况下,指针本身都不是恒定的。

为什么这个操作是非法的?最优雅的工作是什么?


更多详情

如果我通过调用 clang++ --std=c++11 main.cpp 进行编译,则会显示以下错误消息:

main.cpp:19:17: error: no viable overloaded '='
    ptrsToConst = ptrsToObj; //Illegal : but why?!
    ~~~~~~~~~~~ ^ ~~~~~~~~~
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:436:7: note: candidate
      function not viable: no known conversion from 'vector<double *, allocator<double *>>' to 'const
      vector<const double *, allocator<const double *>>' for 1st argument
      operator=(const vector& __x);
      ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:448:7: note: candidate
      function not viable: no known conversion from 'vector<double *, allocator<double *>>' to 'vector<const
      double *, allocator<const double *>>' for 1st argument
      operator=(vector&& __x) noexcept(_Alloc_traits::_S_nothrow_move())
      ^
/usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/bits/stl_vector.h:470:7: note: candidate
      function not viable: no known conversion from 'std::vector<double *>' to 'initializer_list<value_type>' (aka
      'initializer_list<const double *>') for 1st argument
      operator=(initializer_list<value_type> __l)
      ^
1 error generated.

对 gcc (g++) 尝试相同的操作会产生类似的错误消息。

显然,向量的实现方式不允许我尝试执行的操作。但是,这是关于常量正确性的安全操作,对吧?

你可以

这样做,只是不能用operator=. 您需要 assign 成员函数,该函数对每个单独的元素执行转换。

ptrsToConst.assign(ptrsToObj.begin(), ptrsToObj.end());

因为这是实现std::vector的方式:赋值运算符要求其他操作数的类型相同。vector<double *>vector<const double *>是不同的类型,因为即使兼容类型doubleconst double也是不同的。

可以想象,只将这个要求放宽到兼容的类型,但是当标准容器的实现已经足够复杂时(只需读取一次向量标头......),并且库的编写者和标准委员会的编写者都认为没有必要,实现起来会更加复杂。

如果你真的需要它,你将不得不编写一个自定义实现......

我不确定,但也许 c++ 通过引用将std::vector<T*>对象复制到std::vector<const T*>对象。这意味着 c++ 不会为新构造的 std::vector<const T*> 对象分配新内存。这个新构造的std::vector<const T*>对象将是旧std::vector<T*>对象的另一个名称(或引用)。然后,为什么引发错误是有道理的。因为std::vector<T*>对象可以改变它所指向的内容,但是std::vector<const T*>的对象不能改变它所指向的内容。