代码之家  ›  专栏  ›  技术社区  ›  yzt

转发到聚合初始值设定项?

  •  11
  • yzt  · 技术社区  · 7 年前

    在我看来,聚合初始化(合适的类型)并不是一个可以实际调用的构造函数(除了少数情况)。

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

    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 ,)不能简单地(看起来)使用聚合初始化,因为它们使用括号而不是大括号!

    为什么会这样?它只是一个遗漏的特性(还没有人提出/实现它),还是有更深层的原因?这有点奇怪,因为根据定义,聚合类型没有其他构造函数使解析不明确,所以语言可能刚刚定义了一个默认的聚合构造函数(或其他东西),该构造函数将所有成员都作为默认参数。

    只是语法问题吗?如果执行 矢量::放回原处 在上面的例子中使用了placement new 如果用大括号而不是圆括号,它会起作用吗?

    注释 :我要感谢那些指出 vector emplace 因为他们的评论对于那些用这些关键词找到这个问题的人来说是很有价值的,但是我也要指出那些只是例子。我选择了最熟悉和最简洁的示例,但我的重点是在任何代码(或 placement new ,更具体地说。)

    2 回复  |  直到 7 年前
        1
  •  8
  •   Nicol Bolas    7 年前

    为了它的价值, 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 是一种模棱两可的情况。两件事 能够 发生。可以执行的隐式转换 c2 A ,然后初始化 从产生的prvalue。或者可以执行的聚合初始化 按类型的单个值 C .

    p0960采用向后兼容的路由:如果一个构造函数 能够 被调用(在现有规则下),然后总是优先。括号仅在没有可以调用的构造函数时调用聚合初始化。

        2
  •  0
  •   Hanjoung Lee    7 年前

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

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

    根据本文档,您没有定义任何构造函数,也没有满足聚合初始化的其他条件。(请参阅“说明”一节中的“类类型”项),这意味着您的代码不会调用类似自动生成的签名构造函数之类的东西。 Foo(int, int) 但这只是另一个功能。

    文件中提到它的影响是:

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

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

    论点 args... 作为转发给构造函数 std::forward<Args>(args)... .

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

    这样想,代码不能编译也是有意义的 auto p = new Foo (42, 17); .

    如果编写任何类型的构造函数(甚至 Foo::Foo() {} ) auto p = new Foo {42, 17}; 不起作用。因为现在它不满足聚合初始化的条件。

    据我所知,聚合初始化也在C中工作,它甚至不支持构造函数。

    这里是 a good article 值得一读。