为什么可以使用与大括号初始化相同的参数列表来对一个聚合门结构进行大括号初始化,而不是放置?

时间:2017-04-14 08:18:18

标签: c++ c++11 aggregate emplace

好像是this code

#include <string>
#include <vector>

struct bla
{
    std::string a;
    int b;
};

int main()
{
    std::vector<bla> v;
    v.emplace_back("string", 42);
}
在这种情况下,

可以正常工作,但它没有(我理解为什么)。给bla构造函数解决了这个问题,但删除了类型的聚合性,这可能会产生深远的影响。

这是标准中的疏忽吗?或者我错过了某些会在我脸上爆炸的情况,或者它不像我想的那么有用?

2 个答案:

答案 0 :(得分:5)

23.2.1 / 15.5

  

对于零个或多个参数,T是EmplaceConstructible到args中的X.   args,表示以下表达式格式正确:

     

allocator_traits<A>::construct(m, p, args)

23.2.1 / 15

  

[注意:容器调用allocator_traits<A>::construct(m, p, args)以使用args在p处构造元素。 std::allocator中的默认构造将调用::new((void*)p) T(args),但专门的分配器可以选择不同的定义。 - 后注]

因此,默认分配器使用构造函数,更改此行为可能会导致向后兼容性丢失。您可以在此答案https://stackoverflow.com/a/8783004/4759200中阅读更多内容。

还有一个问题"Towards more perfect forwarding"和一些随机discussion关于它的未来。

答案 1 :(得分:5)

  

这是标准中的疏忽吗?

它被认为是标准中的开放缺陷,跟踪为LWG #2089。但是很难解决这个问题。

根本问题来自这样一个事实:你不能只是毫不犹豫地使用braced-init-lists。具有构造函数的类型的列表初始化实际上可以隐藏构造函数,这样某些构造函数就无法通过列表初始化进行调用。这是vector<int> v{1, 2};问题。这会创建一个2元素vector,而不是1元素向量,其唯一元素是2。

因此,您不能在allocator::construct等通用上下文中使用列表初始化。

这将我们带到:

  

我认为如果可能的话,有一个SFINAE技巧可以做到这一点,否则使用也适用于聚合的大括号init。

这需要is_aggregate类型特征。目前尚不存在,没有人提出它的存在。哦,当然,您可以使用is_constructible来解决问题。但问题是:它有效地创建了列表初始化的替代方案。

考虑之前的vector<int>示例。 {1, 2}被解释为两个元素initializer_list。但是通过emplace,它将被解释为调用两个整数构造函数,因为来自这两个元素的is_constructible将是真的。这导致了这个问题:

vector<vector<float>> fvec;
fvec.emplace(1.0f, 2.0f);
vector<vector<int>> ivec;
ivec.emplace(1, 2);

这两个完全不同。fvec情况下,它执行列表初始化,因为vector<float>不能从两个浮点数构造。在ivec的情况下,它调用构造函数,因为vector<int>可以从两个整数构造。

因此,需要allocator::construct中的列表初始化限制为仅在T是聚合时才有效。

即使你这样做了,你也必须将这个SFINAE技巧传播到使用间接初始化的地方的 all 。其中包括any/variant/optional in_placemake_shared/unique构造函数和进驻,allocator::construct调用等等,其中没有一个使用osht=pd.DataFrame(filename+sheetname) delimited table Example: Country;Company;Product US;ABC;XYZ US;ABD;XYY

这并不计算需要这种间接初始化的用户代码。如果用户不进行与C ++标准库相同的初始化操作,那么人们就会感到不安。

这是一个难以解决的问题,它不会将间接初始化API分成允许聚合和组的组。有many possible solutions,但没有一个是理想的。