为什么可以将大集初始化结构大括号初始化,但不能使用与大括号初始化相同的参数列表来放置它? [英] Why can an aggreggate struct be brace-initialized, but not emplaced using the same list of arguments as in the brace initialization?

查看:130
本文介绍了为什么可以将大集初始化结构大括号初始化,但不能使用与大括号初始化相同的参数列表来放置它?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

好像此代码

#include <string>
#include <vector>

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

int main()
{
    std::vector<bla> v;
    v.emplace_back("string", 42);
}

在这种情况下可以正常使用,但不能(我明白为什么)。提供 bla 构造函数可以解决此问题,但可以消除类型的聚合,这可能会产生深远的影响。

could be made to work properly in this case, but it doesn't (and I understand why). Giving bla a constructor solves this, but removes the aggregateness of the type, which can have far-reaching consequences.

这是对标准的疏忽吗?还是我错过了某些情况,这些问题会浮现在我的脸上,还是只是没有我想的有用?

Is this an oversight in the Standard? Or am I missing certain cases where this will blow up in my face, or is it just not as useful as I think?

推荐答案


这是对标准的疏忽吗?

Is this an oversight in the Standard?

被认为是标准中的公开缺陷作为 LWG#2089

It is considered an open defect in the standard, tracked as LWG #2089. But a good solution to it is elusive.

根本的问题来自于这样的事实,即您不能只使用大括号初始化列表willy-nilly。使用构造函数进行类型的列表初始化实际上可以隐藏构造函数,因此某些构造函数可能无法通过列表初始化进行调用。这是 vector< int> v {1,2}; 问题。这样会创建2个元素的 vector ,而不是一个唯一元素为2的1个元素的向量。

The fundamental problem comes from the fact that you cannot just use braced-init-lists willy-nilly. List initialization of types with constructors can actually hide constructors, such that certain constructors can be impossible to call through list initialization. This is the vector<int> v{1, 2}; problem. That creates a 2-element vector, not a 1-element vector whose only element is 2.

这样,您就不能在 allocator :: construct 这样的通用上下文中使用列表初始化。

Because of this, you cannot use list initialization in generic contexts like allocator::construct.

这将我们带到: / p>

Which brings us to:


我认为,如果可能的话,有SFINAE技巧可以做到这一点,否则请采用也可以用于聚合的括号初始化。

I would think there's be a SFINAE trick to do that if possible, else resort to brace init that also works for aggregates.

这将需要 is_aggregate 类型特征。目前尚不存在,也没有人提出它的存在。哦,可以肯定,您可以使用 is_constructible 来解决问题,因为该问题的建议解决方案已说明。但这有一个问题:它有效地创建了列表初始化的替代方法。

That would require an is_aggregate type trait. Which doesn't exist at present, and nobody has proposed its existence. Oh sure, you could make do with is_constructible, as the proposed resolution to the issue states. But there's a problem with that: it effectively creates an alternative to list-initilaization.

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

Consider that vector<int> example from before. {1, 2} is interpreted as a two-element initializer_list. But through emplace, it would be interpreted as calling the two-integer constructor, since is_constructible from those two elements would be true. And that causes this problem:

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

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

These do two completely different things. In the fvec case, it performs list initialization, because vector<float> is not constructible from two floats. In the ivec case, it calls a constructor, because vector<int> is constructible from two integers.

因此您需要 allocator :: construct 中的列表初始化限制为仅在 T 是一个总计。

So you need to limit list initialization in allocator::construct to only work if T is an aggregate.

即使这样做,您也必须将此SFINAE技巧传播到所有使用间接初始化的地方。这包括任何/变量/可选 in_place 构造函数和放置, make_shared / unique 调用,依此类推,都没有使用 allocator :: construct

And even if you did that, you would then have to propagate this SFINAE trick into all of the places where indirect initialization is used. This includes any/variant/optional's in_place constructors and emplacements, make_shared/unique calls, and so forth, none of which use allocator::construct.

不包括需要这种间接初始化的用户代码。如果用户不执行与C ++标准库相同的初始化,则会使人感到沮丧。

And that doesn't count user code where such indirect initialization is needed. If users don't do the same initialization that the C++ standard library does, people will be upset.

这是一个棘手的问题,无法通过解决将间接初始化API分叉成组,以允许聚合和不允许的组。有许多可能的解决方案,但没有一个是理想的。

This is a sticky problem to solve in a way that doesn't bifurcate indirect initialization APIs into groups that allow aggregates and groups that don't. There are many possible solutions, and none of them are ideal.

这篇关于为什么可以将大集初始化结构大括号初始化,但不能使用与大括号初始化相同的参数列表来放置它?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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