当前位置: 首页 > 知识库问答 >
问题:

聚合字段构造函数必须是公共的才能在C++中使用聚合初始化?

田嘉慕
2023-03-14
class A { A(int){} friend struct B; };
struct B { A a{1}; };

int main()
{
    B b; //ok everywhere, not aggregate initialization
    //[[maybe_unused]] B x{1}; //error everywhere
    [[maybe_unused]] B y{}; //ok in GCC and Clang, error in MSVC
}

是GCC和Clang中的bug,还是标准允许他们接受此代码

共有1个答案

裘嘉树
2023-03-14

...使用聚合结构b...

为了完整起见,让我们首先注意b确实是C++14到C++20中的集合,根据[dcl.init.aggr]/1(N4861(2020年3月后布拉格工作草案/C++20 DIS)):

聚合是数组或类([class]),具有

    null

列表-t类型的对象或引用的初始化定义如下:

  • [...]
  • (3.4)否则,如果t是聚合,则执行聚合初始化([dcl.init.aggr])。

对于前一种情况b x{1}b的数据成员a是聚合的显式初始化元素,如[dcl.init.aggr]/3所示。这意味着,根据[dcl.init.aggr]/4,特别是/4.2,数据成员是从initializer子句复制初始化的,这需要在聚合初始化的上下文中构造一个临时的a对象,这使得程序格式不正确,因为a的匹配构造函数是私有的。

B x{1}; // needs A::A(int) to create an A temporary
        // that in turn will be used to copy-initialize
        // the data member a of B.
class A { 
  public:
    static A get() { return {42}; }
  private:
    A(int){}
    friend struct B;
};

struct B { A a{1}; };

int main() {
    auto a{A::get()};
    [[maybe_unused]] B x{a}; // OK
}

对于后一种情况,b y{},根据[dcl.init.aggr]/3.3,b的数据成员a不再是聚合的显式初始化元素,根据[dcl.init.aggr]/5,特别是/5.1

对于非联合聚合,每个不是显式初始化元素的元素的初始化如下:

  • (5.1)如果元素有默认的成员初始值设定项([class.mem]),则从该初始值设定项初始化该元素。
  • [...]

如果我们将私有析构函数添加到A中,那么所有编译器都会显示正确的错误:

由[dcl.init.aggr]/8[强调mine]:

类类型的每个元素的析构函数都可能从发生聚合初始化的上下文中调用([class.dtor])。[注意:此规定确保在引发异常时可以为完全构造的子对象调用析构函数([except.ctor])。-结束注意]

 类似资料:
  • 演示:https://gcc.godbolt.org/z/c33gbqfqh 我在https://en.cppreference.com/w/cpp/language/aggregate_initialization中没有发现任何关于“析构函数”的提及。标准真的要求聚合字段的析构函数对聚合的每个潜在用户都可用吗?

  • 基本上,我试图通过名字和姓氏找到重复的联系人 我错过了什么吗?任何帮助都将不胜感激。 下面是示例文档之一 我试图对Elasticsearch进行如下查询:

  • 我试图理解@bolov对删除默认构造函数问题的第一个公认答案。对象仍然可以创建......有时[1] 似乎我发现了一个错误,所以它搞乱了整个解释。 @bolov解释了为什么这段代码能够在c 11中成功编译: 场景A 以及为什么这段代码无法在c 11中编译: 场景C 他说,重点是第一个foo是聚合,第二个foo不是聚合。 然后他给出了cppreference的摘录: T类型对象的列表初始化的影响是:

  • 我有一张桌子看起来像这个叫“Makerar”的人 并且我要为每个CNAME选择最大平均数值。 所以我这样做 然而,这将不会给出预期的结果,并且下面显示了不正确的输出。 实际结果应该是 我如何解决这个问题? 注意:此表是根据以前的操作创建的视图。

  • 我有一些架构/模式问题。假设我有一个有两个类的域模型。我使用代码优先和存储库模式。(为了使示例更简单,我只使用字段,而不使用属性) 我还有PersonDto和PetDto类,唯一不同的是,PersonDto中的Pets字段是一个简单的List。我使用Automapper将模型类转换为dto。接下来通过REST Api将Dto发送给客户端。现在,如果在某个视图中,我需要为每个人显示其姓名、姓氏和宠物