简体   繁体   English

C ++异常继承歧义

[英]C++ exception inheritance ambiguity

Why does this work? 为什么这样做?

#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();
    }
}

I thought this shouldn't compile due to the ambiguous conversion from abc_error to std::exception . 由于从abc_errorstd::exception的模糊转换,我认为这不应该编译。 What am I missing? 我错过了什么? I came up with the inheritance diagram, and I can't really figure out why this code works (the arrows denote virtual inheritance and the lines denote non-virtual inheritance). 我提出了继承图,我无法弄清楚为什么这个代码有效(箭头表示虚拟继承,而行表示非虚拟继承)。

  std::exception                  std::exception
        +                               +
        |                               |
        |                               |
        +                               +
std::runtime_error              std::runtime_error
        +                               +
        |                               |
        |   +-->boost::exception<-+     |
        +   |                     |     +
   foo_error+<-----+         +--->+bar_error
                   |         |
                   |         |
                   |         |
                   +abc_error+

It looks like abc_error includes two instances of std::exception so the catch (or so I thought) shouldn't be able to cast abc_error to std::exception . 看起来abc_error包含两个std::exception实例,所以catch (或者我认为)不应该将abc_errorstd::exception Or should it? 还是应该呢?

UPDATE UPDATE

I can't answer my own questions at the moment, so I'm going to continue here. 我现在无法回答自己的问题,所以我将继续在这里。 I've narrowed the problem down to: 我把问题缩小到:

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;
}

The sample above works as expected and is a perfectly fine piece of code. 上面的示例按预期工作,是一段非常精细的代码。 It crashes if I replace catch (const VirtualBase& e) with catch (const NonVirtualBase& e) which I think is sane and makes sense. 如果我用catch (const NonVirtualBase& e)替换catch (const VirtualBase& e) ,它会崩溃,我认为它是理智的并且有意义。 But it also works if I replace the same line with catch (const NonVirtualBaseBase& e) which to me seems odd and wrong. 但是如果我用catch (const NonVirtualBaseBase& e)替换相同的行,它也有效,这对我来说似乎catch (const NonVirtualBaseBase& e)奇怪和错误。 A compiler bug? 编译器错误?

UPDATE UPDATE

As pointed out by the OP, this explanation doesn't quite cut it, since std::exception is not derived from using virtual inheritance. 正如OP指出的那样,这种解释并没有完全削减它,因为std::exception不是使用虚拟继承派生的。 I believe the answer to be that this is actually undefined behaviour and simply not caught at compile time because there is no need for the throw and catch to know of each other and warn if they are incompatible. 我相信答案是,这实际上是未定义的行为,并且根本没有在编译时捕获,因为不需要throwcatch来了解彼此并警告它们是否不兼容。

END UPDATE 结束更新

The answer is that this hierarchy uses* virtual inheritance * to derive from boost::exception . 答案是这个层次结构使用* virtual inheritance *从boost::exception派生。

Since both foo_error and bar_error uses virtual inheritance to inherit from boost::exception , there will only be a single boost::exception base which is shared between both the foo_error and bar_error sub-objects of an abc_error . 由于两个foo_errorbar_error使用虚拟继承要继承boost::exception ,将仅存在一个单个boost::exception ,其两者之间共有的底边foo_errorbar_error一个的子对象abc_error

When you specify virtual before an entry in the list of base classes, this means all occurences of this class as a virtual base class in the most derived object will actually refer to the same instance. 当您在基类列表中的条目之前指定virtual ,这意味着此类作为最派生对象中的虚拟基类的所有出现将实际引用同一实例。 It is used specifically to avoid ambiguity in this type of design. 它专门用于避免此类设计的模糊性。

Why does this work? 为什么这样做?

For some loosely defined values of "work". 对于一些松散定义的“工作”值。

me@mybox > g++ -o test test.cpp
me@mybox > ./test
terminate called after throwing an instance of 'abc_error'
Aborted (core dumped)
me@mybox >

It has to compile, because there's no reason you can't have multiply-defined base classes in exceptions, and no reason you can't have that base class in an exception declaration somewhere. 它必须编译,因为没有理由你不能在异常中有多重定义的基类,并且没有理由你不能在某个地方的异常声明中拥有该基类。 The fact that abc happens to throw a particular thing, and that main happens to catch a particular thing, and that those things are frenemies, cannot be compile-time checked. 事实上, abc碰巧抛出一个特定的东西,并且main碰巧捕获一个特定的东西,并且那些东西是敌人,无法进行编译时检查。

What it WON'T do is catch the exception properly, or at least it shouldn't. 它不会做的是正确地捕捉异常,或者至少它不应该。 But I can see how a particular compiler might end up (incorrectly) doing that, because of how exception declarations are matched. 但我可以看到特定编译器如何最终(错误地)执行此操作,因为异常声明如何匹配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM