转发到聚合初始化器?

Forwarding to an Aggregate Initializer?

在我看来,(合适类型的)聚合初始化不被视为您可以实际调用的构造函数(少数情况除外。)

举个例子,如果我们有一个非常简单的聚合类型:

struct Foo {
    int x, y;
};

那么这显然有效:

auto p = new Foo {42, 17};  // not "Foo (42, 17)" though...

但这不适用于我测试过的任何编译器(包括最新版本的 MSVC、GCC 和 Clang):

std::vector<Foo> v;
v.emplace_back(2, 3);

同样,在我看来,任何想要为类型 T 调用构造函数的代码(在这种情况下,vector::emplace_back 中的代码将传递的参数转发给 c' T,) 的 tor 不能简单地(看起来)使用聚合初始化,因为它们使用圆括号而不是大括号!

这是为什么?它只是一个遗漏的功能(还没有 proposed/implemented,)还是有更深层次的原因?这有点奇怪,因为根据定义聚合类型没有其他构造函数来使解析不明确,因此该语言可能只定义了一个默认的聚合构造函数(或其他东西),它将所有成员作为默认参数。

这只是语法问题吗?如果上面示例中 vector::emplace_back 的实现使用带有大括号而不是圆括号的放置 new,它会起作用吗?

注意:我要感谢那些指出 vectoremplace 行为的评论,因为他们的评论对那些人很有价值谁会使用这些关键字找到这个问题,但我还想指出这些只是示例。我选择了最熟悉和最简洁的示例,但我的观点是关于在任何代码中显式调用聚合初始化程序(或在 placement new 中,更具体地说。)

就其价值而言,P0960 "Allow initializing aggregates from a parenthesized list of values" does exactly what it says. It seems to have passed EWG 并且正在进入 C++20。

aggregate types by definition have no other constructor to make the resolution ambiguous

这是不正确的。所有 类 都有默认构造函数,以及 copy/move 构造函数。即使你 = delete 它们或它们被隐式删除,它们在技术上仍然有这样的构造函数(你不能调用它们)。

C++ 是 C++,自然存在极端情况,即使 P0960 也执行 "wrong thing",如论文所述:

struct A;

struct C
{
  operator A(); //Implicitly convertible to `A`
};

struct A { C c; }; //First member is a `C`

C c2;
A a(c2);

a的初始化是一个歧义的情况。有两件事 可能 发生。您可以执行 c2A 的隐式转换,然后从生成的纯右值初始化 a。或者您可以通过 C.

类型的单个值执行 a 的聚合初始化

P0960 采用向后兼容的路线:如果构造函数 可以 被调用(在现有规则下),那么它始终具有优先权。如果没有可以调用的构造函数,括号只会调用聚合初始化。

https://en.cppreference.com/w/cpp/language/aggregate_initialization

聚合初始化不是构造函数。

根据此文档,您没有定义任何构造函数并且满足聚合初始化的其他条件。 (请参阅 "Explanation" 部分中的项目 "class type")这意味着您的代码不会调用自动生成的签名构造函数之类的东西 Foo(int, int) 但它只是另一个功能。

文档说它的作用是:

Each array element, or non-static class member, in order of array subscript/appearance in the class definition, is copy-initialized from the corresponding clause of the initializer list.

因为vector::emplace_back(Args&&... args)是这样工作的,它找不到这样的构造函数。

The arguments args... are forwarded to the constructor as std::forward<Args>(args)....

所以它没有找到这样的构造函数。

这样一想,你的代码无法编译也是有道理的auto p = new Foo (42, 17);

再举个例子,如果你写任何一种构造函数(甚至Foo::Foo() {}),auto p = new Foo {42, 17};都不行。因为现在不满足聚合初始化的条件。

据我所知,聚合初始化也适用于甚至不支持构造函数的 C。

这里 a good article 值得一读。