【问题标题】:Why is it illegal : copying vector of pointers into a vector of pointers to constants为什么它是非法的:将指针向量复制到指向常量的指针向量中
【发布时间】:2017-06-12 05:59:45
【问题描述】:

问题

以下代码无法在 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&lt;T*&gt; 复制到指向常量std::vector&lt;const T*&gt; 的指针向量中。请注意,在这两种情况下,指针本身都不是恒定的。

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


更多详情

如果我通过调用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++) 进行相同尝试会产生类似的错误消息。

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

【问题讨论】:

  • std::vector&lt;const double*&gt; 不是 std::vector&lt;double&gt; 的 cv 合格版本
  • std::vector&lt;const double*&gt;std::vector&lt;double&gt; 是完全不同且不相关的类型。特别是因为你不能保证两者都不是模板类的特殊实现。
  • 如果TU 是不同的类型,则不能将std::vector&lt;T&gt; 分配给std::vector&lt;U&gt;,而不管TU 之间的关系如何。我猜std::vector的设计者没有考虑这个用法。
  • @imreal 实际上std::vector&lt;const double*&gt; 是一种允许(例如)插入/附加新成员的向量类型。但是,该新成员将“内部”存储为 const double*,这意味着它必须是 const double* 或其他可以转换为它的类型,例如 double*
  • @Muscampester:你知道double const*double *const 之间的区别吗? OP 想要第一个,而您建议的解决方案是第二个,这完全不一样。

标签: c++ c++11 pointers vector constants


【解决方案1】:

您可以做到这一点,只是不能使用operator=。您需要 assign 成员函数,它对每个单独的元素执行转换。

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

【讨论】:

  • 也许你想补充一点,这会用非常量向量的副本覆盖 const 向量的内容。而且它不适用于参考分配。
  • @BenVoigt 将std::vector&lt;T*&gt; 复制到std::vector&lt;const T*&gt; 是正常过程吗?或者更确切地说,assign 函数只是 c++ 中不推荐的不寻常复制过程的解决方法?关心assign()的效率和速度
  • @Admia: assign 在需要进行元素转换时使用。它是模板化的,所以它会非常高效/快速。
  • 真正唯一的效率问题是如果assign 不能预分配,但通常库会检查一个随机访问迭代器,其中长度是提前可用的(就像这里一样)。如果您使用的是前向迭代器,则可能会发生重复分配——我希望您随后可以通过reserve() 成员函数提供有关大小的提示。但是库应该足够聪明,可以提前获得这个随机访问迭代器的大小。
  • assign 需要为所有前向和更好的迭代器预先分配。只有输入的迭代器(可能是单通道)才允许重新分配。
【解决方案2】:

因为这是std::vector 的实现方式:赋值运算符要求另一个操作数的类型相同。而vector&lt;double *&gt;vector&lt;const double *&gt; 是不同的类型,因为doubleconst double 即使兼容类型也不同。

可以想象只对兼容类型放宽该要求,但实现起来会更复杂,因为标准容器的实现已经足够复杂(只需读取一次向量头......),而且无论是图书馆和标准委员会都认为有必要。

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 不,它不这样做。
    猜你喜欢
    • 2011-08-04
    • 2021-06-19
    • 1970-01-01
    • 1970-01-01
    • 2014-02-23
    • 2020-10-23
    • 1970-01-01
    • 1970-01-01
    • 2020-04-13
    相关资源
    最近更新 更多