可以使用 C++ 聚合初始化来构造实现接口的 class 的实例吗?
Can C++ aggregate initialization be used to construct an instance of a class which implements an interface?
我希望有人能告诉我为什么以下代码无法编译的技术细节,如果可能的话,解决方法。
我有一个名为 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
An aggregate is an array or a class (Clause 12) with
1.1 no user-provided, explicit, or inherited constructors (15.1),
1.2 no private or protected non-static data members (Clause 14),
1.3 no virtual functions (13.3), and
1.4 no virtual, private, or protected base classes (13.1).
虽然我实际上不确定这里是否发生了聚合初始化。有人可以解释发生的错误,如果可能的话,提供我可以对界面进行的更改吗?我有几个现有的结构需要这个接口,还有很多使用这种初始化形式的现有代码,我想尽可能少地重写它。谢谢!
你需要初始化基础 class 即使它是空的:
Foo f({{},1,2});
在您所指部分的标准中,我们可以在 [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};
initializes d1.b1 with 1, d1.b2 with 2, d1.b3 with 42, d1.d with 4,
and d2.b1 with 0, d2.b2 with 42, d2.b3 with 42, d2.d with 4. —end
example]
另请参阅 [dcl.init.aggr]p2,其中解释了聚合的元素是什么:
The elements of an aggregate are:
-for an array, the array elements in increasing subscript order, or
-for a class, the direct base classes in declaration order, followed by the direct non-static data members ([class.mem]) that are not
members of an anonymous union, in declaration order.
When an aggregate is initialized by an initializer list as specified in [dcl.init.list], the elements of the initializer list are taken as initializers for the elements of the aggregate. ...
注意,答案假定 C++17 或更高版本,因为 before C++17 an aggregate was not allowed to have a base class。
@ShafikYaghmour 解释了为什么当 Interface
为空时,无法像以前那样进行聚合初始化。
但是如果 Interface
有虚函数,如问题中所建议的,从 Interface
派生的 class 将不是聚合 .因此,实现 Interface
并将数据成员保存为 Foo
的 class 必须实现一个构造函数。我看到的最简单的方法(根据数据成员的 "triviality",就速度而言可能不是最有效的)是这样的:
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;
}
我希望有人能告诉我为什么以下代码无法编译的技术细节,如果可能的话,解决方法。
我有一个名为 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 An aggregate is an array or a class (Clause 12) with 1.1 no user-provided, explicit, or inherited constructors (15.1), 1.2 no private or protected non-static data members (Clause 14), 1.3 no virtual functions (13.3), and 1.4 no virtual, private, or protected base classes (13.1).
虽然我实际上不确定这里是否发生了聚合初始化。有人可以解释发生的错误,如果可能的话,提供我可以对界面进行的更改吗?我有几个现有的结构需要这个接口,还有很多使用这种初始化形式的现有代码,我想尽可能少地重写它。谢谢!
你需要初始化基础 class 即使它是空的:
Foo f({{},1,2});
在您所指部分的标准中,我们可以在 [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};
initializes d1.b1 with 1, d1.b2 with 2, d1.b3 with 42, d1.d with 4, and d2.b1 with 0, d2.b2 with 42, d2.b3 with 42, d2.d with 4. —end example]
另请参阅 [dcl.init.aggr]p2,其中解释了聚合的元素是什么:
The elements of an aggregate are:
-for an array, the array elements in increasing subscript order, or
-for a class, the direct base classes in declaration order, followed by the direct non-static data members ([class.mem]) that are not members of an anonymous union, in declaration order.
When an aggregate is initialized by an initializer list as specified in [dcl.init.list], the elements of the initializer list are taken as initializers for the elements of the aggregate. ...
注意,答案假定 C++17 或更高版本,因为 before C++17 an aggregate was not allowed to have a base class。
@ShafikYaghmour 解释了为什么当 Interface
为空时,无法像以前那样进行聚合初始化。
但是如果 Interface
有虚函数,如问题中所建议的,从 Interface
派生的 class 将不是聚合 .因此,实现 Interface
并将数据成员保存为 Foo
的 class 必须实现一个构造函数。我看到的最简单的方法(根据数据成员的 "triviality",就速度而言可能不是最有效的)是这样的:
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;
}