简体   繁体   English

为什么emplace_back()不使用统一初始化?

[英]Why doesn't emplace_back() use uniform initialization?

The following code: 以下代码:

#include <vector>

struct S
{
    int x, y;
};

int main()
{
    std::vector<S> v;
    v.emplace_back(0, 0);
}

Gives the following errors when compiled with GCC: 使用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&&'

Suggesting that vector is using regular () constructor syntax to construct the element from the arguments to emplace_back() . 建议该vector使用regular ()构造函数语法从emplace_back()的参数构造元素。 Why doesn't vector use the {} uniform-initialization syntax instead, to make examples like the above work? 为什么vector不使用{}统一初始化语法来制作像上面那样的例子呢?

It seems to me that there is nothing to lose by using {} (it calls the constructor when there is one, but still works when there isn't one), and it would be more in the spirit of C++11 to use {} - after all, the whole point of uniform initialization is that it is used uniformly - that is, everywhere - to initialize objects. 在我看来,使用{} (没有任何东西可以在有一个时调用构造函数,但在没有构造函数时仍然可以工作),并且它更符合C ++ 11的精神使用{} - 毕竟, 统一初始化的全部意义在于它统一使用 - 即无处不在 - 来初始化对象。

Great minds think alike ;v) . 伟大的思想相似; v)。 I submitted a defect report and suggested a change to the standard on this very topic. 我提交了一份缺陷报告,并建议对此主题的标准进行更改。

http://cplusplus.github.com/LWG/lwg-active.html#2089 http://cplusplus.github.com/LWG/lwg-active.html#2089

Also, Luc Danton helped me understand the difficulty: Direct vs uniform initialization in std::allocator . 此外,Luc Danton帮助我理解了难度: std :: allocator中的直接与统一初始化

When the EmplaceConstructible (23.2.1 [container.requirements.general]/13) requirement is used to initialize an object, direct-initialization occurs. 当EmplaceConstructible(23.2.1 [container.requirements.general] / 13)要求用于初始化对象时,会发生直接初始化。 Initializing an aggregate or using a std::initializer_list constructor with emplace requires naming the initialized type and moving a temporary. 初始化聚合或使用带有emplace的std :: initializer_list构造函数需要命名初始化类型并移动临时类型。 This is a result of std::allocator::construct using direct-initialization, not list-initialization (sometimes called "uniform initialization") syntax. 这是使用直接初始化而不是列表初始化(有时称为“统一初始化”)语法的std :: allocator :: construct的结果。

Altering std::allocator::construct to use list-initialization would, among other things, give preference to std::initializer_list constructor overloads, breaking valid code in an unintuitive and unfixable way — there would be no way for emplace_back to access a constructor preempted by std::initializer_list without essentially reimplementing push_back. 改变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 

The proposed compromise is to use SFINAE with std::is_constructible, which tests whether direct-initialization is well formed. 建议的折衷方案是使用带有std :: is_constructible的SFINAE,它测试直接初始化是否形成良好。 If is_constructible is false, then an alternative std::allocator::construct overload is chosen which uses list-initialization. 如果is_constructible为false,则选择使用列表初始化的备用std :: allocator :: construct重载。 Since list-initialization always falls back on direct-initialization, the user will see diagnostic messages as if list-initialization (uniform-initialization) were always being used, because the direct-initialization overload cannot fail. 由于列表初始化始终依赖于直接初始化,因此用户将看到诊断消息,就像始终使用列表初始化(统一初始化)一样,因为直接初始化过载不会失败。

I can see two corner cases that expose gaps in this scheme. 我可以看到两个角落案例,揭示了这个方案中的空白。 One occurs when arguments intended for std::initializer_list satisfy a constructor, such as trying to emplace-insert a value of {3, 4} in the above example. 当用于std :: initializer_list的参数满足构造函数时会出现这种情况,例如在上面的示例中尝试插入值{3,4}。 The workaround is to explicitly specify the std::initializer_list type, as in v.emplace_back(std::initializer_list(3, 4)). 解决方法是显式指定std :: initializer_list类型,如v.emplace_back(std :: initializer_list(3,4))。 Since this matches the semantics as if std::initializer_list were deduced, there seems to be no real problem here. 由于这与语义匹配,好像推断了std :: initializer_list,因此这里似乎没有真正的问题。

The other case is when arguments intended for aggregate initialization satisfy a constructor. 另一种情况是,用于聚合初始化的参数满足构造函数。 Since aggregates cannot have user-defined constructors, this requires that the first nonstatic data member of the aggregate be implicitly convertible from the aggregate type, and that the initializer list have one element. 由于聚合不能具有用户定义的构造函数,因此这要求聚合的第一个非静态数据成员可以从聚合类型隐式转换,并且初始化列表具有一个元素。 The workaround is to supply an initializer for the second member. 解决方法是为第二个成员提供初始化程序。 It remains impossible to in-place construct an aggregate with only one nonstatic data member by conversion from a type convertible to the aggregate's own type. 通过从可转换为聚合类型的类型转换,就不可能就地只构建一个只有一个非静态数据成员的聚合。 This seems like an acceptably small hole. 这似乎是一个可以接受的小洞。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM