Why can an aggreggate struct be brace-initialized, but not emplaced using the same list of arguments as in the brace initialization?

别来无恙 提交于 2020-01-01 04:48:10

问题


It seems like this code:

#include <string>
#include <vector>

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

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

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?


回答1:


Is this an oversight in the Standard?

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

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.

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

Which brings us to:

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

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.

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);

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.

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

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.

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.

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.




回答2:


23.2.1/15.5

T is EmplaceConstructible into X from args, for zero or more arguments args, means that the following expression is well-formed:

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

23.2.1/15

[Note: A container calls allocator_traits<A>::construct(m, p, args) to construct an element at p using args. The default construct in std::allocator will call ::new((void*)p) T(args), but specialized allocators may choose a different definition. —end note ]

So, default allocator uses a constuctor, changing this behavior could cause backward compatibility loss. You could read more in this answer https://stackoverflow.com/a/8783004/4759200.

Also there is an issue "Towards more perfect forwarding" and some random discussion about it's future.



来源:https://stackoverflow.com/questions/43407814/why-can-an-aggreggate-struct-be-brace-initialized-but-not-emplaced-using-the-sa

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!