10

我希望有人能给我为什么以下内容无法编译的技术细节,如果可能的话,一个解决方法。

我有一个名为 Foo 的现有结构,以及使用初始化列表创建 Foo 实例的代码。此代码编译并工作:

struct Foo {
    int id1;
    int id2;
};

int main()
{
    Foo f({1,2});

    return f.id1;
}

我希望 Foo 实现一个接口:

struct Interface {
    // All pure virtual methods, but this won't compile even if empty
};

struct Foo : public Interface{
    int id1;
    int id2;
};

int main()
{
    Foo f({1,2});

    return f.id1;
}

此代码不再编译,出现以下错误

cannot convert argument 1 from 'initializer list' to 'const _Ty &'

(错误会根据您的确切编译器而变化。)

我找到了与聚合初始化相关的标准的这一部分:

[dcl.init.aggr]/1 聚合是一个数组或一个类(第 12 条),其中 1.1 没有用户提供的、显式的或继承的构造函数(15.1),1.2 没有私有或受保护的非静态数据成员(第 14 条)、1.3 没有虚函数 (13.3) 和 1.4 没有虚拟、私有或受保护的基类 (13.1)。

虽然我实际上不确定聚合初始化是否是这里发生的事情。有人可以解释正在发生的错误,如果可能的话,提供我可以对界面进行的更改吗?我有几个需要这个接口的现有结构,以及许多使用这种初始化形式的现有代码,我想尽可能少地重写它。谢谢!

4

2 回答 2

11

即使它是空的,您也需要初始化基类:

Foo f({{},1,2});

看到它在godbolt上直播

在您所指的部分的标准中,我们可以在[dcl.init.aggr]p4.2中看到一个示例:

struct base1 { int b1, b2 = 42; };
struct base2 {
  base2() {
   b3 = 42;
 }
 int b3;
};

struct derived : base1, base2 {
 int d;
};

derived d1{{1, 2}, {}, 4};
derived d2{{}, {}, 4};

用 1 初始化 d1.b1,用 2 初始化 d1.b2,用 42 初始化 d1.b3,用 4 初始化 d1.d,用 0 初始化 d2.b1,用 42 初始化 d2.b2,用 42 初始化 d2.b3,用 4 初始化 d2.d。 ——结束示例]

另请参阅[dcl.init.aggr]p2,它解释了聚合的元素是什么:

聚合的元素是:

- 对于数组,按递增下标顺序排列的数组元素,或
-对于类,按声明顺序排列的直接基类,后跟不是匿名成员的直接非静态数据成员 ([class.mem])联合,按声明顺序。

[dcl.init.aggr]p3说:

当聚合被 [dcl.init.list] 中指定的初始化列表初始化时,初始化列表的元素被视为聚合元素的初始化。...

请注意,答案假定 C++17 或更高版本,因为在 C++17 之前,不允许聚合具有基类

于 2018-12-08T01:01:28.773 回答
1

@ShafikYaghmour 解释了为什么当Interface为空时,聚合初始化不能像以前那样进行。

但是,如果Interface有虚函数,正如问题中所建议的那样,派生类 fromInterface 将不是一个 aggregate。因此,实现Interface和保存数据成员的类Foo必须实现构造函数。我看到的最简单的方法(根据数据成员的“琐碎性”,在速度方面可能不是最有效的)是这样的:

struct Interface {
   // All pure virtual methods, but this won't compile even if empty
   virtual void bar() =0;
   };

struct Foo_data{ //change the name of the aggregate
  int id1;
  int id2;
  };

struct Foo
  :Interface  //Foo has virtual function => Foo is not an aggregate
  ,Foo_data
  {
  Foo() =default;
  Foo(Foo_data data):Foo_data(std::move(data)){}//a constructor must be provided
  void bar() override {}
  };

int main(){
  Foo f({1,2});
  return f.id1;
  }
于 2018-12-08T10:20:48.847 回答