以下代码:
#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&&'
建议vector
使用常规()
构造函数语法从emplace_back()
的参数构造元素。为什么vector
不使用{}
统一初始化语法来制作上述工作的示例?
在我看来,使用{}
没有什么可失去的(当有一个构造函数时它会调用构造函数,但是当没有构造函数时它仍然可以工作),并且它更符合精神C ++ 11使用{}
- 毕竟,统一初始化的重点在于统一使用 - 即无处不在 - 来初始化对象。
答案 0 :(得分:49)
伟大的思想相似; v)。我提交了一份缺陷报告,并建议对此主题的标准进行更改。
http://cplusplus.github.com/LWG/lwg-active.html#2089
此外,Luc Danton帮助我理解了难度:Direct vs uniform initialization in std::allocator。
当EmplaceConstructible(23.2.1 [container.requirements.general] / 13)要求用于初始化 一个对象,发生直接初始化。初始化聚合或 使用带有emplace的std :: initializer_list构造函数需要命名 初始化类型并移动临时。这是结果 使用直接初始化的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
建议的妥协是使用SFINAE和std :: is_constructible, 它测试直接初始化是否形成良好。如果 is_constructible是假的,然后是另一种选择 选择std :: allocator :: construct重载使用 列表初始化。由于列表初始化总是重新开始 直接初始化,用户将看到诊断消息,就像 始终使用列表初始化(统一初始化), 因为直接初始化过载不会失败。
我可以看到两个角落案例,揭示了这个方案中的空白。一个发生 当用于std :: initializer_list的参数满足a时 构造函数,例如尝试在{3,4}中插入值{3,4} 上面的例子。解决方法是明确指定 std :: initializer_list类型,如 v.emplace_back(std :: initializer_list(3,4))。由于这匹配 似乎std :: initializer_list被推导出来的语义 这里不是真正的问题。
另一种情况是用于聚合初始化的参数 满足构造函数。由于聚合不能具有用户定义 构造函数,这需要第一个非静态数据成员 聚合可以从聚合类型隐式转换,和 初始化列表有一个元素。解决方法是 为第二个成员提供初始化程序。它仍然是不可能的 就地构造一个只有一个非静态数据成员的聚合 从可转换类型转换为聚合自己的类型。这个 似乎是一个可以接受的小洞。