向量中不同类型的统一初始化行为不同

Posted

技术标签:

【中文标题】向量中不同类型的统一初始化行为不同【英文标题】:Uniform initialization behavior different for different types in vector 【发布时间】:2015-07-23 17:52:06 【问题描述】:

我遇到了this 文章,我在其中阅读了其中一位海报的这个例子。为了方便,我在这里引用了。

struct Foo

    Foo(int i)  // #1
    Foo() 
;

int main()

    std::vector<Foo> f 10;

    std::cout << f.size() << std::endl;

上面的代码,如所写,发出“1”(10 是由 a 转换为 Foo 构造函数接受一个 int,然后是向量的 initializer_list 构造函数被调用)。如果我注释掉注释为 #1 的行,则 结果是“10”(initializer_list 无法转换,因此 int 使用构造函数)。

我的问题是,如果删除了 int 构造函数,为什么它会发出 10。 我了解统一初始化列表按以下顺序工作

1-Calls the initializer list if available or possible
2-Calls the default constructor if available
3-Does aggregate initialization

在上述情况下,为什么它在向量中创建 10 个项目,因为 1,2 和 3 是不可能的?这是否意味着通过统一初始化,项目向量可能总是具有不同的行为?

【问题讨论】:

您的术语似乎不对。默认 c'tor 是可以在没有参数的情况下调用的那个(如果有多个这样的 c'tor,由于默认参数,那么使用它的尝试将是模棱两可的)。不能在这里调用它,因为这需要空的或没有括号/大括号。聚合初始化仅适用于没有构造函数的 POD,不适用于 std::vector(例如用于 std::array 和其他 C 样式结构)。如果您将 2. 更改为“调用具有兼容参数类型的构造函数”,它会更有意义,但聚合 init.和 c'tors 真的是非此即彼。 澄清一下(我用完了字符):聚合初始化绝不是调用某些构造函数的后备。仅当类或结构根本没有构造函数时才可用。因此,如果一个类有一个构造函数,但既不能调用带有 std::initializer_list 的构造函数,也不能调用具有兼容参数的构造函数,则不会回退到聚合初始化。相反,程序格式错误。 @ArneVogel 答案属于答案 - 不要写多段 cmets... 【参考方案1】:

借用Scott Meyers 在Effective Modern C++ 中的一段话(强调原文):

但是,如果一个或多个构造函数声明了std::initializer_list 类型的参数,则使用大括号初始化语法的调用强烈倾向于采用std;:initializer_lists 的重载。 强烈。如果编译器有任何方式将使用大括号初始化程序的调用解释为采用std::initializer_list 的构造函数,编译器将采用该解释。

所以当你有std::vector&lt;Foo&gt; f 10; 时,它会尝试使用vector&lt;Foo&gt; 的构造函数,它接受initializer_list&lt;Foo&gt;。如果Foo 可以从int 构造,那就是我们正在使用的构造函数——所以我们最终会得到一个从10 构造的Foo

或者,从标准的角度来看,在 [over.match.list] 中:

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

(1.1) — 最初,候选函数是类 T 的初始化列表构造函数 (8.5.4) 和 参数列表由作为单个参数的初始值设定项列表组成。 (1.2) — 如果没有找到可行的初始化列表构造函数,则再次执行重载决议,其中 候选函数是类T的所有构造函数,参数列表由元素组成 初始化列表。

如果有 一个可行的初始化列表构造函数,则使用它。如果你没有 Foo(int ) 构造函数,就不会有一个可行的初始化列表构造函数,并且第二次重载解析会发现 vector 的构造函数需要一个大小 - 所以你会得到一个代替 10 个默认构造的 Foos 的向量。

【讨论】:

以上是关于向量中不同类型的统一初始化行为不同的主要内容,如果未能解决你的问题,请参考以下文章

[C++11]统一的数据初始化方式 - 初始化列表

QWidget初始化函数调用中的不同行为

使用统一初始化 初始化二维向量

[C++11] --- 易用性改进

总结3

go:内存分配