为什么emplace_back()不使用统一的初始化? [英] Why doesn't emplace_back() use uniform initialization?

查看:267
本文介绍了为什么emplace_back()不使用统一的初始化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码:

#include <vector>

struct S
{
    int x, y;
};

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

使用GCC编译时出现以下错误:

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

建议向量使用常规()构造函数语法从参数 emplace_back()。为什么不能向量使用 {} 统一初始化语法,以上例的工作?

Suggesting that vector is using regular () constructor syntax to construct the element from the arguments to emplace_back(). Why doesn't vector use the {} uniform-initialization syntax instead, to make examples like the above work?

在我看来,使用 {} 没有任何损失一个,但仍然工作,当没有一个),并将更多的精神在C + + 11使用 {} - 毕竟,整点

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.

推荐答案

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

此外,Luc Danton帮助我了解困难: std :: allocator中的Direct vs uniform初始化

Also, Luc Danton helped me understand the difficulty: Direct vs uniform initialization in std::allocator.


当EmplaceConstructible(23.2.1
[container.requirements.general] / 13)需求用于初始化
一个对象,发生直接初始化。使用带emplace的std :: initializer_list构造函数初始化聚合或
需要命名
初始化类型并移动一个临时数。这是
std :: allocator :: construct使用直接初始化而不是
list-initialization(有时称为统一初始化)
语法的结果。

When the EmplaceConstructible (23.2.1 [container.requirements.general]/13) requirement is used to initialize an object, direct-initialization occurs. Initializing an aggregate or using a std::initializer_list constructor with emplace requires naming the initialized type and moving a temporary. This is a result of std::allocator::construct using direct-initialization, not list-initialization (sometimes called "uniform initialization") syntax.

改变std :: allocator :: construct使用list-initialization
将会优先使用std :: initializer_list
构造函数重载,破坏有效的代码unintuitive和
不合适的方式 - 没有办法emplace_back访问一个
构造函数preved由std :: initializer_list基本上没有
重新实现push_back。

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::vector<std::vector<int>> v;
v.emplace_back(3, 4); // v[0] == {4, 4, 4}, not {3, 4} as in list-initialization


b $ b

提出的妥协是使用带有std :: is_constructible,
的SFINAE来测试直接初始化是否正确。如果
is_constructible为false,那么选择一个替代的
std :: allocator :: construct重载,它使用
list-initialization。由于列表初始化总是落在
直接初始化,用户将看到诊断消息,如同
list-initialization(uniform-initialization)总是被使用,
因为直接初始化重载不能失败。

The proposed compromise is to use SFINAE with std::is_constructible, which tests whether direct-initialization is well formed. If is_constructible is false, then an alternative std::allocator::construct overload is chosen which uses list-initialization. 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.

我可以看到在这个方案中暴露缺口的两个角点。当std :: initializer_list的参数满足一个
构造函数时,例如试图emplace,在
上面的例子中插入一个{3,4}的值时,就会出现
。解决方法是显式指定
std :: initializer_list类型,如
v.emplace_back(std :: initializer_list(3,4))中所示。因为这匹配
的语义,就像std :: initializer_list被推导出来,似乎
在这里没有真正的问题。

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. The workaround is to explicitly specify the std::initializer_list type, as in 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.

另一种情况是当用于聚合初始化的参数
满足构造函数时。由于聚合不能有用户定义的
构造函数,这就要求
的第一个非静态数据成员聚合可以从聚合类型隐式转换,
初始化列表有一个元素。解决方法是
为第二个成员提供初始化程序。仍然不可能
就地构造一个只有一个非静态数据成员的聚合,通过
从可转换为聚合体自身类型的类型转换。这
似乎是一个可以接受的小洞。

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.

这篇关于为什么emplace_back()不使用统一的初始化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆