【问题标题】:Inheriting constructors and virtual base classes继承构造函数和虚拟基类
【发布时间】:2013-10-24 08:16:32
【问题描述】:

我即将创建一个异常类层次结构,它在概念上看起来有点像这样:

#include <iostream>
#include <stdexcept>

class ExceptionBase : public std::runtime_error {
public: 
    ExceptionBase( const char * msg ) : std::runtime_error(msg) {}
};

class OperationFailure : virtual public ExceptionBase {
public: 
    using ExceptionBase::ExceptionBase;
};

class FileDoesNotExistError : virtual public ExceptionBase {
public: 
    using ExceptionBase::ExceptionBase;
};

class OperationFailedBecauseFileDoesNotExistError
    : public OperationFailure, FileDoesNotExistError {
public: 
    using ExceptionBase::ExceptionBase; // does not compile
};

int main() {
    OperationFailedBecauseFileDoesNotExistError e("Hello world!\n");

    std::cout << e.what();
}

所有构造函数的外观都应与ExceptionBase 类的构造函数相同。派生的异常仅在类型上有所不同,否则没有附加功能。上面代码中提到的最后一个异常类型也应该有这些构造函数。这可能使用 C++11 标准的继承构造函数功能吗?如果这是不可能的:有什么替代方案?

(顺便说一句:在上面的代码中,OperationFailureFileDoesNotExistError 类不是用 gcc 4.8 编译的,而是用 clang 3.4 编译的。显然,gcc 拒绝继承虚拟基的构造函数。知道谁是就在这里。两个编译器都拒绝了 OperationFailedBecauseFileDoesNotExistError 类,因为继承的构造函数不是从直接基类继承的。)

【问题讨论】:

  • 钻石继承?甜蜜。
  • 乐趣始于 3、2、1...
  • 也许您需要使用using OperationFailure::OperationFailure?但可能由于双重继承,它不会起作用。
  • 在创建对象(使用参数化 ctor)时,即使是最近的 g++4.9 也无法编译 OperationFailure。当用非虚拟替换虚拟继承时,它可以工作。我建议您为 gcc 提交错误报告,因为我在标准中找不到任何禁止这样做的内容,而最新提案 N2540 明确允许这样做。
  • 我会提交错误报告。

标签: c++ c++11 virtual-inheritance inheriting-constructors


【解决方案1】:

继承构造函数就像为您指定的所有构造函数引入包装函数。在您的情况下,您必须调用 OperationFailureFileDoesNotExistError 的特定构造函数,但引入的包装器只会调用它们中的任何一个。


我刚刚检查了the latest C++11 draft(第 12.9 节),但它并没有真正明确地涵盖您的情况。

【讨论】:

  • 默认ctors的隐式声明不受using-declaration的禁止,因此OperationFailureFileDoesNotExistError仍然有默认ctors。但是我不确定它们是否格式正确,因为它们没有正确初始化虚拟基类。
  • 如果OperationFailedBecauseFileDoesNotExistError(我们称这个类为O)中的这个using-declaration是合法的,它会引入一个相当于O(char const* msg) : ExceptionBase( static_cast&lt;char const*&amp;&amp;&gt;(msg) ) {}的ctor。但是,这个 ctor 格式错误,因为它调用了两个直接基类的已删除默认 ctor。后者格式错误(已删除),因为它们没有初始化虚拟基类,请参阅 [class.ctor]/5。
  • (另见DR1567)。
【解决方案2】:

using-declaration用于继承构造函数时,需要直接基类[namespace.udecl]/3

如果这样的using-declaration命名了一个构造函数,nested-name-specifier应该命名一个被定义类的直接基类;否则它会引入通过成员名称查找找到的声明集。

即在您的情况下,OperationFailedBecauseFileDoesNotExistError 中的 using-declaration 不会继承,而是重新声明(作为别名)或取消隐藏 ExceptionBase 的 ctor 的名称。

您必须为 OperationFailedBecauseFileDoesNotExistError 编写一个非继承 ctor。


顺便说一句,这对于非虚拟基类来说很好:继承 ctor 的 using-declaration 重写为:

//using ExceptionBase::ExceptionBase;

OperationFailure(char const * msg)
: ExceptionBase( static_cast<const char*&&>(msg) )
{}

由于您只能在 mem-initializer-list 中初始化直接基类(或虚拟基类),因此非虚拟基类限制 使用-声明只从直接基类继承ctors。

继承 ctors 提案的作者已经意识到这会破坏对虚拟基类 ctors 的支持,请参阅N2540

通常,为具有虚拟基的类继承构造函数定义将是不正确的,除非虚拟基支持默认初始化,或者虚拟基是直接基,并命名为转发到的基。同样,所有数据成员和其他直接基必须支持默认初始化,否则任何使用继承构造函数的尝试都将是错误的。注意:使用时格式错误,未声明。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-06-21
    • 2018-04-18
    • 2021-06-02
    • 1970-01-01
    • 2012-12-19
    • 2017-12-05
    • 1970-01-01
    相关资源
    最近更新 更多