为啥 'std::vector<int> b2;'创建一个 1 元素向量,而不是 2 元素向量?

Posted

技术标签:

【中文标题】为啥 \'std::vector<int> b2;\'创建一个 1 元素向量,而不是 2 元素向量?【英文标题】:Why does 'std::vector<int> b2;' create a 1-element vector, and not a 2-element one?为什么 'std::vector<int> b2;'创建一个 1 元素向量,而不是 2 元素向量? 【发布时间】:2012-04-01 03:55:44 【问题描述】:

过去几天我一直在玩 C++11,但我想出了一些奇怪的东西。

如果我想统一初始化一个int:

int a5;

但是如果我对 std::vector 做同样的事情:

std::vector<int> b2;

不构造一个二元素数组,而是构造一个具有一个值为 2 的元素的数组。似乎要获得这种效果,需要更加明确:

std::vector<int> c2;
std::vector<int> d = 2;

但不像 b 的声明 - 这似乎不一致。我见过一些其他的东西达到同样的效果。我要问的是——这种行为是在最终的 C++11 标准中,还是只是在早期实施的草案中?如果是这样,为什么标准委员会会包括这种行为?似乎它违背了统一初始化的全部目的,因为必须记住哪些类具有初始化列表构造函数,并且仅对这些类使用旧的 () 语法而不是 。或者完全放弃统一初始化。

这似乎是一个很大的“陷阱”。但它可能有一些我不知道的优点。

编辑:这段代码:

#include <iostream>
#include <vector>

int main() 
    std::vector<int> a2;
    for (auto x: a) 
        std::cout << x << std::endl;
    
    return 0;

在 gcc 4.6.2 上输出“2”

【问题讨论】:

@ildjarn:我可以在 gcc 上确认这一点,并且由于它有一个初始化列表 ctor,这似乎是正确的做法。 我不是 C++11 专家,但我只是参加了有关它的培训课程,我觉得它很合适。 @PlasmaHH 我知道这是一个虚假的承诺。但似乎更难记住特定类型是否可以为所有类型提供初始化列表构造函数(也可能会改变),而不是只记住类型的基本“类”(原始、结构、类、枚举等),不应经常更改。 @RobertMason:这可能是一个好主意,仅当您指的是初始化列表时才使用 @Mooring Duck:但这正是它的作用。这被提升为一个功能。我想要的是,如果他们要实现这样的功能,他们是一致的。如果他们要允许列表初始化调用任意构造函数,那么(恕我直言)他们应该需要双括号来调用初始化列表构造函数以与其他语法保持一致。 【参考方案1】:

统一初始化并不意味着你认为它做了什么。添加它是为了使 C++ 中的类型之间的初始化更加统一。原因是这样的:

typedef struct dog_ 
   float height;
   int weight;
 dog;
int main()  
    dog Spot =  25.6, 45;
    dog Data[3] =  Spot, 6.5, 7 ;
    std::array<dog, 2> data =   Spot, 6.5, 7  ; //only in C++ obviously
    return 0;

这是valid C and C++ code,已经很多年了真的很方便,但您必须记住,这只适用于 POD 类型。人们抱怨很久没有办法做到std::vector&lt;int&gt; data = 3, 7, 4, 1, 8;,但是有些类(std::array)写的很奇怪,允许初始化列表构造函数。

所以对于 C++11,委员会做出了这样的安排,以便我们可以让向量和其他很酷的类也这样做。这使得所有类型的构造更加统一,因此我们可以使用 通过构造函数进行初始化,也可以从值列表中进行初始化。您遇到的问题是带有std::initializer_list&lt;int&gt; 的构造函数重载是最佳匹配,将首先被选中。因此,std::vector&lt;int&gt; b2; 并不意味着调用采用int 的构造函数,而是意味着从int 值列表中创建一个vector。鉴于此,创建一个包含单个值2vector 是完全合理的。要调用不同的构造函数,您必须使用 () 语法,以便 C++ 知道您不想从列表中初始化。

【讨论】:

"构造函数不是用代替(),那太傻了。没有只采用 std::initializer_list&lt;&gt; 实例的构造函数重载。 Stroustrup 在他的常见问题解答www2.research.att.com/~bs/C++0xFAQ.html#uniform-init 中提到统一初始化适用于所有初始化。但也有例外(如std::initializer_lists),您必须使用“旧式”。 @ildjarn:IMO 不应该。如果有人可以向我解释为什么它应该调用其他构造函数,I'm willing to listen。与此同时,我把那句话改成了没有争议的内容。 @Mooing :它存在于不同的上下文中,但如果以前由于语法错误而无法编译的东西现在在新标准下编译,我认为调用新的合法的东西是安全的语法“新”。 @evnu:我刚刚查看了他的常见问题解答,并没有看到任何暗示 应该用于所有构造函数的内容。他说 被允许用于所有初始化,但这并不意味着它可以访问所有构造函数,也不应该总是使用它。我将其解释为 现在在所有初始化上下文中都有效。【参考方案2】:

是的,根据 §13.3.1.7 通过列表初始化进行初始化,此行为是有意的

当非聚合类类型 T 的对象被列表初始化时 (8.5.4),重载决议分两个阶段选择构造函数:

——最初,候选函数是初始化列表 T 类的构造函数(8.5.4)和参数列表包括 初始化器列表作为单个参数。

——如果没有可行的 找到初始化列表构造函数,重载决议是 再次执行,其中候选函数都是 T 类的构造函数和参数列表由 初始化列表的元素。

至于“统一初始化的全部目的”……“统一初始化”是一个营销术语,并不是一个很好的描述。该标准具有所有常见的初始化形式加上列表初始化,但没有“统一初始化”。列表初始化并不是初始化的终极形式,它只是实用工具带中的另一个工具。

【讨论】:

好的。感谢您的澄清。为什么标准委员会不要求人们编写 std::vector a(2);?这样就不需要让编译器实现一个全新的初始化语法并将其应用于原语。如果目的是让人们在更多地方使用 ,那么这似乎是一个反特征。 () 和 之前各有各的位置。但是现在, 可以一直使用,除非它不能使用。所以我的直觉反应是完全避免使用新功能并使用旧语法来避免烦人的陷阱。 好的。刚看到你的编辑。不幸的是,每个人都在兜售列表初始化作为 c++ 初始化的全部和结束,然后将其吹捧为一个巨大的特性。我不一定认为这是最直观或最一致的行为,但我没有编写标准是有原因的:P @RobertMason:这是一个巨大的功能,但绝不是“全部结束”。我很不高兴人们这么认为,因为这不是真的。 @RobertMason:使用旧语法给您带来的“烦人的陷阱”就像“最令人烦恼的解析”之类的东西。这比想要在具有有效初始化列表的类上调用非初始化列表构造函数的少数和相对较小的情况要痛苦得多。毕竟,您真正使用vector&lt;int&gt; 的频率是多少?对于非整数(或浮点)类型的 vector,这不会发生。 @NicolBolas 你知道为什么他们决定改变重载决议的规则吗?让() 选择不同的构造函数对我来说似乎非常不正常。为什么他们不强制要求std::vector&lt;int&gt; a1,2(双组大括号)而不是更改重载分辨率?它看起来如此不一致,就像他们在自找麻烦(想到在模板实用程序函数中创建对象,如 std::make_unique)。【参考方案3】:

标准规定初始化列表构造函数优先于其他构造函数。这只是一种不可能将() 替换为 的情况。还有其他的,例如 初始化不允许缩小转换。

【讨论】:

是的,C++11 §13.3.1.7/1 支持。 是的,但对我来说,这相当于采用 std::vector a(2) 然后说 std::vector a2 做同样的事情。这对我来说似乎不太一致。统一初始化的重点是允许更多的一致性 AFAIK。那么为什么要添加这个呢? @Robert Mason,我想人们可以在这里阅读“统一”,这意味着您可以初始化 std::vector、std::array、c 样式数组、std::set、std::以相同的方式列出和用户定义的集合类型。我并不是说它很棒,我花了一段时间才理解它。

以上是关于为啥 'std::vector<int> b2;'创建一个 1 元素向量,而不是 2 元素向量?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 std::set::find 不提供提示迭代器?

为啥我不能增加 std::unordered_map 迭代器?

为啥 std::vector<bool> 没有 .data()?

对“std::vector<int, std::allocator<int>>”类型空指针的引用绑定

运行时错误:引用绑定到“std::vector<int, std::allocator<int>>”类型的空指针 (stl_vector.h)

运行时错误:引用绑定到“std::vector<int, std::allocator<int> >”类型的空指针 (stl_vector.h)