向量中不同类型的统一初始化行为不同
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_list
s 的重载。 强烈。如果编译器有任何方式将使用大括号初始化程序的调用解释为采用std::initializer_list
的构造函数,编译器将采用该解释。
所以当你有std::vector<Foo> f 10;
时,它会尝试使用vector<Foo>
的构造函数,它接受initializer_list<Foo>
。如果Foo
可以从int
构造,那就是我们正在使用的构造函数——所以我们最终会得到一个从10
构造的Foo
。
或者,从标准的角度来看,在 [over.match.list] 中:
当非聚合类类型
T
的对象被列表初始化(8.5.4)时,重载决议选择构造函数 分两个阶段:(1.1) — 最初,候选函数是类
T
的初始化列表构造函数 (8.5.4) 和 参数列表由作为单个参数的初始值设定项列表组成。 (1.2) — 如果没有找到可行的初始化列表构造函数,则再次执行重载决议,其中 候选函数是类T
的所有构造函数,参数列表由元素组成 初始化列表。
如果有 一个可行的初始化列表构造函数,则使用它。如果你没有 Foo(int )
构造函数,就不会有一个可行的初始化列表构造函数,并且第二次重载解析会发现 vector
的构造函数需要一个大小 - 所以你会得到一个代替 10 个默认构造的 Foo
s 的向量。
【讨论】:
以上是关于向量中不同类型的统一初始化行为不同的主要内容,如果未能解决你的问题,请参考以下文章