转发到聚合初始化程序?

时间:2019-02-09 12:57:59

标签: c++ c++17 c++20

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

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

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中的代码会将传递的参数转发给T,)(看起来)不能简单地使用聚合初始化,因为它们使用括号而不是花括号!

那是为什么?是仅仅是一项遗漏的功能(尚无人提议/实施),还是有更深层次的原因?这有点奇怪,因为按照定义,聚合类型没有其他构造函数可以使解析度变得模棱两可,因此该语言可能已经定义了一个默认的聚合构造函数(或其他任何东西),它将所有成员作为默认参数。

这仅仅是语法问题吗?如果以上示例中的vector::emplace_back的实现使用了带有大括号而不是括号的位置new,是否行得通?

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

2 个答案:

答案 0 :(得分:9)

对于其价值,P0960 "Allow initializing aggregates from a parenthesized list of values"完全按照其说的去做。它似乎有passed EWG,并且正在进入C ++ 20。

  

按照定义,聚合类型没有其他构造函数可以使分辨率模糊

那是不正确的。所有类都具有默认构造函数以及复制/移动构造函数。即使您= delete,或者它们被隐式删除,从技术上讲,它们仍然具有此类构造函数(您无法调用它们)。

C ++是C ++,自然有一些极端的情况,即使P0960也会做“错误的事情”,如本文所述:

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的隐式转换,然后从结果prvalue初始化a。或者,您可以通过类型为a的单个值执行C的聚合初始化。

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

答案 1 :(得分:0)

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

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

根据本文档,您未定义任何构造函数且未满足聚合初始化的其他条件。 (请参阅“说明”部分中的“类类型”项。)这意味着您的代码不会调用自动生成的签名JPanel构造函数之类的东西,而仅仅是另一种功能。

该文件说明其效果是:

  

每个数组元素或非静态类成员,按照类定义中数组下标/外观的顺序,从初始化列表的相应子句中进行复制初始化。

JFrame以这种方式工作,因此找不到此类构造函数。

  

参数Foo(int, int)作为vector::emplace_back(Args&&... args)转发给构造函数。

因此找不到此类构造函数。

这样思考,代码就不能编译args...也很有意义。

再举一个例子,如果您编写任何类型的构造函数(甚至是std::forward<Args>(args)...),auto p = new Foo (42, 17);都不起作用。因为现在它不满足聚合初始化的条件。

据我所知,聚合初始化也可以在不支持构造函数的C语言中使用。

a good article值得一读。