为什么这样做?
#include <exception>
#include <iostream>
#include <stdexcept>
#include <boost/exception/all.hpp>
struct foo_error : virtual boost::exception, public std::runtime_error
{
explicit foo_error(const char* what)
: std::runtime_error(what)
{ }
explicit foo_error(const std::string& what)
: std::runtime_error(what)
{ }
};
struct bar_error : virtual boost::exception, public std::runtime_error
{
explicit bar_error(const char* what)
: std::runtime_error(what)
{ }
explicit bar_error(const std::string& what)
: std::runtime_error(what)
{ }
};
struct abc_error : virtual foo_error, virtual bar_error
{
explicit abc_error(const char* what)
: foo_error(what), bar_error(what)
{ }
explicit abc_error(const std::string& what)
: foo_error(what), bar_error(what)
{ }
};
static void abc()
{
throw abc_error("abc error");
}
int main()
{
try
{
abc();
}
catch (const std::exception& e)
{
std::cerr << e.what();
}
}
Run Code Online (Sandbox Code Playgroud)
我认为这不应该编译,因为模糊转换abc_error为std::exception.我错过了什么?我提出了继承图,我无法弄清楚为什么这个代码有效(箭头表示虚拟继承,而行表示非虚拟继承).
std::exception std::exception
+ +
| |
| |
+ +
std::runtime_error std::runtime_error
+ +
| |
| +-->boost::exception<-+ |
+ | | +
foo_error+<-----+ +--->+bar_error
| |
| |
| |
+abc_error+
Run Code Online (Sandbox Code Playgroud)
它看起来像abc_error包括两个实例std::exception所以catch(或因此我认为)应该不能投abc_error给std::exception.还是应该呢?
UPDATE
我现在无法回答自己的问题,所以我将继续在这里.我把问题缩小到:
struct NonVirtualBaseBase { };
struct NonVirtualBase : NonVirtualBaseBase { };
struct VirtualBase { };
struct A : virtual VirtualBase, NonVirtualBase { };
struct B : virtual VirtualBase, NonVirtualBase { };
struct C : A, B { };
int main()
{
try
{
throw C();
}
catch (const VirtualBase& e)
{
return 1;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
上面的示例按预期工作,是一段非常精细的代码.如果我catch (const VirtualBase& e)用catch (const NonVirtualBase& e)我认为理智而且有意义的替换它会崩溃.但是,如果我替换同样的线条catch (const NonVirtualBaseBase& e)对我来说似乎有些奇怪也是有效的.编译器错误?
更新
正如OP所指出的,这种解释并没有完全消除它,因为std::exception不是从使用虚拟继承派生的。throw我相信答案是,这实际上是未定义的行为,并且根本没有在编译时捕获,因为和不需要catch彼此了解并警告它们是否不兼容。
结束更新
答案是这个层次结构使用*虚拟继承*派生自boost::exception.
由于 和foo_error都bar_error使用虚拟继承来继承boost::exception,因此只有一个基类在和 的子对象boost::exception之间共享。foo_errorbar_errorabc_error
当您virtual在基类列表中的条目之前指定时,这意味着该类在最派生对象中作为虚拟基类的所有出现实际上都将引用同一实例。它专门用于避免此类设计中的歧义。