为什么不emplace_back()使用统一的初始化?
以下代码:
#include <vector> struct S { int x, y; }; int main() { std::vector<S> v; v.emplace_back(0, 0); }
使用GCC编译时出现以下错误:
In file included from c++/4.7.0/i686-pc-linux-gnu/bits/c++allocator.h:34:0, from c++/4.7.0/bits/allocator.h:48, from c++/4.7.0/vector:62, from test.cpp:1: c++/4.7.0/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = S; _Args = {int, int}; _Tp = S]': c++/4.7.0/bits/alloc_traits.h:265:4: required from 'static typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = S; _Args = {int, int}; _Alloc = std::allocator<S>; typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type = void]' c++/4.7.0/bits/alloc_traits.h:402:4: required from 'static void std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = S; _Args = {int, int}; _Alloc = std::allocator<S>]' c++/4.7.0/bits/vector.tcc:97:6: required from 'void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int, int}; _Tp = S; _Alloc = std::allocator<S>]' test.cpp:11:24: required from here c++/4.7.0/ext/new_allocator.h:110:4: error: new initializer expression list treated as compound expression [-fpermissive] c++/4.7.0/ext/new_allocator.h:110:4: error: no matching function for call to 'S::S(int)' c++/4.7.0/ext/new_allocator.h:110:4: note: candidates are: test.cpp:3:8: note: S::S() test.cpp:3:8: note: candidate expects 0 arguments, 1 provided test.cpp:3:8: note: constexpr S::S(const S&) test.cpp:3:8: note: no known conversion for argument 1 from 'int' to 'const S&' test.cpp:3:8: note: constexpr S::S(S&&) test.cpp:3:8: note: no known conversion for argument 1 from 'int' to 'S&&'
build议vector
使用regular ()
构造函数语法从参数构造元素emplace_back()
。 为什么不使用{}
统一初始化语法代替上述工作?
在我看来,通过使用{}
它没有任何损失(当有构造函数时,它会调用构造函数,但在没有构造函数时仍然有效),并且更多地使用C ++ 11的精神来使用{}
– 毕竟, 统一初始化的要点就是统一使用 – 即无处不在 – 来初始化对象。
伟大的思想一致;五)。 我提交了一份缺陷报告,并build议在这个主题上修改标准。
http://cplusplus.github.com/LWG/lwg-active.html#2089
而且,Luc Danton帮助我理解了这个难点: 在std :: allocator中直接vs统一初始化 。
当EmplaceConstructible(23.2.1 [container.requirements.general] / 13)需求被用来初始化一个对象时,直接初始化发生。 初始化一个聚合或使用一个std :: initializer_list构造函数与emplace需要命名初始化types和移动临时。 这是使用直接初始化而不是列表初始化(有时称为“统一初始化”)语法的std :: allocator :: construct的结果。
改变std :: allocator :: construct来使用列表初始化将会优先考虑std :: initializer_list构造函数重载,以不直观和不可修复的方式破坏有效代码 – emplace_back将无法访问构造函数被std :: initializer_list抢占,而不会实质上重新实现push_back。
std::vector<std::vector<int>> v; v.emplace_back(3, 4); // v[0] == {4, 4, 4}, not {3, 4} as in list-initialization
build议的折衷办法是使用SFINAE和std :: is_constructible来testing直接初始化是否正确。 如果is_constructible为false,则select使用列表初始化的替代std :: allocator :: construct重载。 由于列表初始化总是落在直接初始化上,所以用户将看到诊断消息,就像总是使用列表初始化(统一初始化)一样,因为直接初始化过载不会失败。
我可以看到两个案例揭露了这个计划的缺陷。 其中一种情况是当std :: initializer_list的参数满足一个构造函数时,例如在上面的例子中试图插入一个{3,4}的值。 解决方法是显式指定std :: initializer_listtypes,如v.emplace_back(std :: initializer_list(3,4))。 由于这与std :: initializer_list被推导出来的语义相匹配,这里似乎没有真正的问题。
另一种情况是当用于聚合初始化的参数满足构造函数时。 由于聚合不能具有用户定义的构造函数,因此这要求聚合的第一个非静态数据成员可以从聚合types中隐式转换,并且初始化程序列表只有一个元素。 解决方法是为第二个成员提供一个初始化程序。 通过从可转换为集合自己的types的types转换,只有一个非静态数据成员就地构build一个聚合仍然是不可能的。 这似乎是一个可以接受的小洞。