C ++ 11是否允许vector <const T>?


81

容器要求已从C ++ 03更改为C ++ 11。尽管C ++ 03具有全面的要求(例如,副本的可构造性和向量的可分配性),但C ++ 11定义了每个容器操作的细粒度要求(第23.2节)。

结果,您可以例如将可复制构造但不可分配的类型(例如具有const成员的结构)存储在向量中,只要您仅执行某些不需要分配的操作(构造且push_back为此类操作)即可;insert不是)。

我想知道的是:这是否意味着该标准现在允许vector<const T>?我看不出有任何理由不应该-const T与具有const成员的结构一样,是一种可复制构造但不能分配的类型-但我可能错过了一些东西。

(让我觉得我可能错过了一些东西的部分原因是,如果您尝试实例化vector<const T>,gcc主干会崩溃并烧毁,但是vector<T>对于T具有const成员的地方也很好)。

Answers:


54

不,我相信分配器要求说T可以是“非常量,非引用对象类型”。

向量常量对象将无法做很多事情。const vector<T>无论如何,a几乎是一样的。


许多年后,这个肮脏的答案仍然吸引着评论和投票。并不总是起来。:-)

因此,添加一些适当的参考:

对于我在纸上提出的C ++ 03标准,[lib.allocator.requirements]节中的表31表示:

T, U any type

并不是说任何类型都能实际工作。

因此,下一个标准C ++ 11在[allocator.requirements]和表27的封闭草案中说:

T, U, C any non-const, non-reference object type

这与我上面从内存中最初写的非常接近。这也是问题所在。

但是,在C ++ 14(N4296草案)中,表27现在说:

T, U, C any non-const object type

可能是因为引用毕竟不是对象类型吗?

现在在C ++ 17(草案N4659)中,表30表示:

T, U, C any cv-unqualified object type (6.9)

因此,不仅const排除在外,而且volatile。无论如何可能都是旧消息,只是澄清一下。


另请参阅当前位于下方的Howard Hinnant的第一手信息


42
底线:我们没有设计容纳const T的容器。尽管我确实考虑了一下。我们真的很偶然地做到了。据我所知,当前的症结是address默认分配器中的一对重载成员函数:当T为const时,这两个重载具有相同的签名。纠正此问题的简单方法是专门化std::allocator<const T>并消除其中的一个重载。
霍华德·辛南特

4
@ HighCommander4:我不是很肯定。在libc ++上,我可以使用协作分配器构造一个向量(甚至是非空向量)。我不能做其他任何事情(非常量)。我不确定这是否符合您对“作品”的定义。如果我不经意地利用扩展程序,我也不会很乐观。为了确定,我将需要在这个问题上花费更多的时间。我之前进行过这样的投资,但是那是几年前的事,在此期间很多事情都发生了变化。如果确实有效,那不是委员会的设计。
霍华德·辛南特

1
@霍华德:我想不出任何技术上的障碍push_back。但是,如果设计不允许这样做,最好不要这样做。我只是好奇而已。
HighCommander4 2011年

7
缓存通常是不可变对象的可变容器,而排序后的向量通常是映射的替代方法,因此我不同意const对象的向量没什么用。
克里斯·奥尔德伍德

9
这是一个耻辱。我之所以使用std::vector<const T>它,是因为它与非常相似const std::vector<T>,但是没有后者对持有它的类的负面影响。实际上,std::vector<const T>在大多数情况下,这正是我在语义上所需要的vector。现在,我必须放弃const它所提供的可靠性。
紫罗兰色长颈鹿

29

更新资料

在2011年,我接受并接受了正确的答案:

底线:我们没有设计要容纳的容器const T。虽然我确实考虑了一下。我们真的很偶然地做到了。据我所知,目前的症结是一对重载address默认allocator的成员函数:如果Tconst,这两个重载具有相同的签名。纠正此问题的一种简单方法是专门化 std::allocator<const T>并消除过载之一。

在即将到来的C ++ 17草案中,对我来说,我们现在已经合法化了vector<const T>,我也相信我们偶然做到了。:-)

P0174R0从中消除了address过载std::allocator<T>P0174R0没有提及支持std::allocator<const T>是其基本原理的一部分。

更正

在下面的注释中,TC正确地指出了address重载已被弃用,而不是被移除。我的错。弃用的成员不会显示在20.10.9中std::allocator定义了的成员中,而是被降级到D.9节。发布此消息时,我忽略了扫描D章以了解这种可能性。

谢谢TC的纠正。我打算删除此误导性答案,但最好还是保留此更正,以免其他人以与我相同的方式误读规范。


6
真有趣!(现在,我们只需要对此保持安静,就可以让它滑入C ++ 17,而无需任何人注意:))
HighCommander4 2016年

分配器需求表是否还只是完全禁止它?无论如何,P0174R2(已投赞成票的修订版)仅弃用而不删除address
TC

@TC:你绝对正确。感谢您的更正。
Howard Hinnant

所以c ++ 2x最终将允许vector<const T>:)
MM

1
答案“底线:我们没有将容器设计为容纳constT。”假定目标是容器应容纳“ const T”。但是,可能有人认为用户的目标是限制对容器的操作-这样,例如'back()'返回“ const T&”-无论容器包含什么内容。
汉斯·奥尔森,2016年

8

即使我们已经对此做出了很好的回答,但我还是决定做出更实际的回答,以表明可以做什么和不能做什么。

因此,这不起作用:

vector<const T> vec; 

只需阅读其他答案即可了解原因。而且,正如您可能已经猜到的,这也不起作用:

vector<const shared_ptr<T>> vec;

T不再const,而是vector持有shared_ptr,而不是T

另一方面,这确实有效:

vector<const T *> vec;
vector<T const *> vec;  // the same as above

但是在这种情况下,const是指向的对象,而不是指针本身(向量存储的对象)。这等效于:

vector<shared_ptr<const T>> vec;

没关系

但是,如果我们将const表达式放在末尾,它现在会将指针变成一个const,因此以下内容将无法编译:

vector<T * const> vec;

我同意,这有点令人困惑,但是您已经习惯了。


4

作为其他答案的补充,另一种方法是使用:

vector<unique_ptr<const T>> vec;

如果是这种情况,您要强制执行,则仅vec对其项目拥有所有权。或者,如果您希望动态移动项目,vec并在某些时候将其移出。

正如指出的那样,指针const的语义可能会产生混淆,但shared_ptrunique_ptr没有。const unique_ptr<T>是一个const指针,并且unique_ptr<const T>是您期望的const指针。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.