我很抱歉标题晦涩难懂,不知道如何更好地表达。
考虑以下继承层次结构:
struct A
{
A(int) {}
};
struct B : virtual A
{
B() : A(42) {}
};
struct C : B
{
C() : A(43) {}
};
它确实有效。现在让我们说,我想创建一个可以透明地注入层次结构中间的模板,如下所示:
template <typename ...P>
struct Proxy : P...
{
// This constructor doesn't change anything. It was added
// to indicate that `Proxy` should only be used as a base.
protected:
Proxy() = default;
};
struct D : Proxy<B>
{
D() : A(44) {}
};
这给了我:error: call to implicitly-deleted default constructor of 'Proxy<B>'
。
在gcc.godbolt.org上运行
我理解发生了什么:Proxy
不能有默认的构造函数,因为它没有为A
提供初始化器,因此派生类不能默认构造Proxy
。
但仔细想想,这是没有意义的,因为即使我在Proxy
中为A
提供了一个初始化器,D
也会忽略它,并且必须提供自己的初始化器。
如何绕过此限制?
代码中的所有内容都可以更改,但我更喜欢侵入性较小的更改。
在我的实际代码中,只有一个基类会导致这些问题,所以我为Proxy
(与requires
不同(创建了两个不同的默认构造函数:一个什么都不做,另一个(当P...
中的任何一个实际上继承自A
时使用(将一个伪参数传递给A::A(int)
。
但我不喜欢这个解决方案,因为它不是通用的,所以我正在寻找更好的替代方案。
[special]/7:
对于类、其非静态数据成员、其非虚拟直接基类,如果该类不是抽象的([class.abstract](,则其虚拟基类被称为潜在构造的子对象。
[class.default.cctor]/2.7表示,如果
任何潜在构建的子对象,非静态数据除外具有大括号或相等初始值设定项的成员,具有类类型
M
(或数组其(并且M
没有默认构造函数或重载应用于查找M
对应的分辨率([over.match](构造函数导致歧义或函数被删除或无法从默认的默认构造函数访问
因此,我们可以通过使Proxy
抽象来从潜在构建的子对象集中排除虚拟基,例如,通过使析构函数纯虚拟(但仍提供定义(:
template <typename ...P>
struct Proxy : P...
{
protected:
virtual ~Proxy() = 0;
Proxy() = default;
};
template <typename ...P>
Proxy<P...>::~Proxy() = default;