为什么可以将大集初始化结构大括号初始化,但不能使用与大括号初始化相同的参数列表来放置它? [英] Why can an aggreggate struct be brace-initialized, but not emplaced using the same list of arguments as in the brace initialization?
问题描述
好像此代码:
#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屋!