【问题标题】:Why does std::vector transfer its constness to the contained objects?为什么 std::vector 将其常量转移到包含的对象?
【发布时间】:2011-08-16 23:52:39
【问题描述】:

const int *int *const 非常不同。 const std::auto_ptr<int>std::auto_ptr<const int> 类似。但是,const std::vector<int>std::vector<const int> 似乎没有这种区别(实际上我不确定是否允许使用第二个)。这是为什么呢?

有时我有一个函数,我想将引用传递给向量。该函数不应该修改向量本身(例如,没有push_back()),但它想修改每个包含的值(例如,增加它们)。类似地,我可能希望一个函数只更改向量结构而不修改其任何现有内容(尽管这很奇怪)。这种事情用std::auto_ptr(例如)是可能的,但是因为std::vector::front()(例如)被定义为

const T &front() const;
T &front();

不仅仅是

T &front() const;

没有办法表达。

我想做的例子:

//create a (non-modifiable) auto_ptr containing a (modifiable) int
const std::auto_ptr<int> a(new int(3));

//this works and makes sense - changing the value pointed to, not the pointer itself
*a = 4;
//this is an error, as it should be
a.reset();


//create a (non-modifiable) vector containing a (modifiable) int
const std::vector<int> v(1, 3);

//this makes sense to me but doesn't work - trying to change the value in the vector, not the vector itself
v.front() = 4;
//this is an error, as it should be
v.clear();

【问题讨论】:

  • 我没有看到有人提到过这一点,但是指针的 const 向量允许您更改指针指向的对象,并且可以在需要时用作解决方法。跨度>

标签: c++ stl vector constants


【解决方案1】:

这是一个设计决定。

如果你有一个const 容器,通常你不希望任何人修改它包含的元素,这是它固有的一部分。如果你愿意的话,容器完全“拥有”这些元素“巩固了联系”。

这与历史悠久的、更底层的“容器”实现(即原始数组)形成对比,后者更加不干涉。正如您所说的那样,int const*int * const 之间存在很大差异。但标准容器只是选择传递constness。

【讨论】:

  • 我认为这是有道理的,有些容器(出于这个问题的目的,我认为std::auto_ptr 一个容器)这样做,而其他容器却没有,这让我感到很奇怪。它似乎打破了“当有疑问时,像整数一样做”的一般哲学。
  • @Karu:如果您想将std::auto_ptr 视为一个容器,那么这是您的选择……但这并不能改变它不是容器的事实。 :)
【解决方案2】:

不同之处在于指向 int 的指针不拥有它们指向的 int,而 vector&lt;int&gt; 确实拥有包含的 int。 vector&lt;int&gt; 可以概念化为具有 int 成员的结构,其中成员的数量恰好是可变的。

如果您想创建一个可以修改向量中包含的值但不能修改向量本身的函数,那么您应该将函数设计为接受迭代器参数。

例子:

void setAllToOne(std::vector<int>::iterator begin, std::vector<int>::iterator end)
{
    std::for_each(begin, end, [](int& elem) { elem = 1; });
}

如果您有能力将所需的功能放在标题中,则可以将其设为通用:

template<typename OutputIterator>
void setAllToOne(OutputIterator begin, OutputIterator end)
{
    typedef typename iterator_traits<OutputIterator>::reference ref;
    std::for_each(begin, end, [](ref elem) { elem = 1; });
}

【讨论】:

    【解决方案3】:

    您的建议在语法上的一个大问题是:std::vector&lt;const T&gt;std::vector&lt;T&gt; 的类型不同。因此,您不能将 vector&lt;T&gt; 传递给需要 vector&lt;const T&gt; 而不进行某种转换的函数。不是简单的演员表,而是创建一个新的vector&lt;const T&gt;。新的不能简单地与旧的共享数据;它必须将数据从旧数据复制或移动到新数据。

    您可以使用std::shared_ptr 解决这个问题,但那是因为它们是共享 指针。你可以有两个对象引用同一个指针,所以从std::shared_ptr&lt;T&gt;shared_ptr&lt;const T&gt; 的转换不会受到伤害(除了增加引用计数)。没有 shared_vector 这样的东西。

    std::unique_ptr 也可以工作,因为它们只能被移动,不能被复制。因此,只有其中一个会拥有指针。

    所以你所要求的根本不可能。

    【讨论】:

    • 您不能拥有std::vector&lt;const T&gt;。元素类型必须是可复制构造和可分配的。干杯&hth.,
    • @Alf:我知道 std::vector 目前是如何工作的。提问者问为什么它不能以另一种方式工作,即 std::vector 实际上会起作用。
    • 通过从 const 特化派生非 const 是可能的:ideone.com/BYOnl。最大的问题是缺少我们不希望 standard 容器使用的虚拟析构函数(但如果您要设计容器,我想应该没问题)。还有其他可能的问题吗?
    【解决方案4】:
    • 你是对的,不可能有一个 const int 的向量,主要是因为元素不可分配(对向量中包含的元素类型的要求)。
    • 如果您想要一个仅修改向量元素但不向向量本身添加元素的函数,这主要是 STL 为您所做的 - 具有不知道元素序列包含在哪个容器中的函数. 该函数只需要一对迭代器并为该序列执行其操作,完全没有注意到它们包含在向量中这一事实。
    • 查找“插入迭代器”以了解如何将某些内容插入容器,而无需知道元素是什么。例如,back_inserter 接受一个容器,它所关心的只是知道该容器有一个名为“push_back”的成员函数。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-02-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多