C++ 模糊虚固有
对于应用程序,我需要创建一组特殊类来处理异常。我从std::exception派生了我的基类。然而,我最终面临钻石问题和模棱两可的继承。即使使用虚拟继承也没有帮助。下面的示例演示了该问题C++ 模糊虚固有,c++,c++11,c++14,C++,C++11,C++14,对于应用程序,我需要创建一组特殊类来处理异常。我从std::exception派生了我的基类。然而,我最终面临钻石问题和模棱两可的继承。即使使用虚拟继承也没有帮助。下面的示例演示了该问题 #include <iostream> class Exception: public virtual std::exception { public: Exception(): std::exception("This is default") {
#include <iostream>
class Exception:
public virtual std::exception
{
public:
Exception():
std::exception("This is default")
{
std::cout << "This is Exception\n";
}
};
class ChildException:
public virtual std::runtime_error,
public Exception
{
public:
ChildException():
Exception(),
std::runtime_error("hello")
{
std::cout << "This is ChildException\n";
}
};
int main()
{
ChildException exc();
//std::cout << static_cast<std::exception> (exc).what() << std::endl;
//std::cout << static_cast<std::runtime_error> (exc).what() << std::endl;
getchar();
return 0;
}
#包括
类异常:
公共虚拟std::异常
{
公众:
异常():
std::exception(“这是默认值”)
{
std::cout目前我可以看到两个问题:
1:最烦人的解析
正如评论中所指出的,这一行实际上是一个函数原型:
ChildException exc();
它可以被读取为“<代码> CyrExcExpTo> <代码>命名为代码> EXC/<代码>,它是通过调用默认构造函数初始化的,或者作为一个名为“代码> EXC/<代码>的函数返回一个<代码> CyrExcRebug ;我不确定确切的原因,但是C++标准规定,在这种情况下,它将被读取为后者。< /P>
有三种方法可以解决这个问题:
- 删除括号:如果您只是调用默认构造函数,则可以不使用括号直接编写。然而,这并不总是一个选项,因为当您尝试使用通过函数调用获得的值进行直接初始化时,最麻烦的解析也会让您措手不及
ChildException exc;
// Most vexing parse:
ChildException ce;
ChildException ce2(ce);
// This is safe, it can't be read as a function prototype.
ChildException ce3(ChildException());
// This will be parsed as a function with:
// Return type: ChildException
// Parameter: Function pointer of type "ChildException (*)()".
- 使用复制初始化:您可以使用赋值语法对其进行初始化,编译器将通过复制省略对其进行优化
ChildException exc = ChildException();
这是可行的,但看起来不必要的笨拙,如果遇到无法执行复制省略的编译器,则可能会降低效率
ChildException exc = ChildException();
使用统一初始化:从C++11开始,当使用支持统一初始化*的编译器时,可以使用大括号而不是括号来指定构造函数调用;考虑到问题的标记,我建议使用这种方法
ChildException exc{};
*[在三个“最大”中编译器,Clang 3.1或更高版本、GCC 4.6或更高版本以及Visual Studio 2013或更高版本支持统一初始化。虽然GCC从4.4开始支持统一初始化,而Visual Studio从2012 CTP开始支持统一初始化,但早期版本在某些情况下遇到了困难;我不确定Clang的早期版本是否存在问题。]
2:钻石问题
我假设您遇到问题的代码是两行注释:
//std::cout << static_cast<std::exception> (exc).what() << std::endl;
//std::cout << static_cast<std::runtime_error> (exc).what() << std::endl;
请注意,如果您愿意,虽然您的异常
实际上继承自std::Exception
,std::runtime\u error
。因此,它的std::Exception
基与您的Exception
基不同,因此任何将ChildException
强制转换为std::exception
将是不明确的,因为它可能引用ChildException::exception
基或ChildException::runtime\u error::exception
基。如果可能,我建议重构您的异常类,使每个异常类最多继承一个std
异常类。I如果不可能,可以通过基类之一强制转换:
// Cast into std::exception through the base classes:
std::cout << "As Exception: "
<< static_cast<std::exception>(static_cast<Exception>(exc)).what()
<< std::endl;
std::cout << "As runtime_error: "
<< static_cast<std::exception>(static_cast<std::runtime_error>(exc)).what()
<< std::endl;
//通过基类强制转换为std::exception:
std::cout目前我可以看到两个问题:
1:最烦人的解析
正如评论中指出的,这一行实际上是一个功能原型:
ChildException exc();
它可以被读取为“<代码> CyrExcExpTo> <代码>命名为代码> EXC/<代码>,它是通过调用默认构造函数初始化的,或者作为一个名为“代码> EXC/<代码>的函数返回一个<代码> CyrExcRebug ;我不确定确切的原因,但是C++标准规定,在这种情况下,它将被读取为后者。< /P>
有三种方法可以解决这个问题:
- 删除括号:如果您只是调用默认构造函数,则可以不使用括号直接编写。然而,这并不总是一个选项,因为当您尝试使用通过函数调用获得的值进行直接初始化时,最麻烦的解析也会让您措手不及
ChildException exc;
// Most vexing parse:
ChildException ce;
ChildException ce2(ce);
// This is safe, it can't be read as a function prototype.
ChildException ce3(ChildException());
// This will be parsed as a function with:
// Return type: ChildException
// Parameter: Function pointer of type "ChildException (*)()".
- 使用复制初始化:您可以使用赋值语法对其进行初始化,编译器将通过复制省略对其进行优化
ChildException exc = ChildException();
这是可行的,但看起来不必要的笨拙,如果遇到无法执行复制省略的编译器,则可能会降低效率
ChildException exc = ChildException();
使用统一初始化:从C++11开始,当使用支持统一初始化*的编译器时,可以使用大括号而不是括号来指定构造函数调用;考虑到问题的标记,我建议使用这种方法
ChildException exc{};
*[在三个“最大”中编译器,Clang 3.1或更高版本、GCC 4.6或更高版本以及Visual Studio 2013或更高版本支持统一初始化。虽然GCC从4.4开始支持统一初始化,而Visual Studio从2012 CTP开始支持统一初始化,但早期版本在某些情况下遇到了困难;我不确定Clang的早期版本是否存在问题。]
2:钻石问题
我假设您遇到问题的代码是两行注释:
//std::cout << static_cast<std::exception> (exc).what() << std::endl;
//std::cout << static_cast<std::runtime_error> (exc).what() << std::endl;
请注意,如果您愿意,虽然您的异常
实际上继承自std::Exception
,std::runtime\u error
。因此,它的std::Exception
基与您的Exception
基不同,因此任何将ChildException
强制转换为std::exception
将是不明确的,因为它可能引用ChildException::exception
基或ChildException::runtime\u error::exception
基。如果可能,我建议重构您的异常类,使每个异常类最多继承一个std
异常类。I如果不可能,可以通过基类之一强制转换:
// Cast into std::exception through the base classes:
std::cout << "As Exception: "
<< static_cast<std::exception>(static_cast<Exception>(exc)).what()
<< std::endl;
std::cout << "As runtime_error: "
<< static_cast<std::exception>(static_cast<std::runtime_error>(exc)).what()
<< std::endl;
//通过基类强制转换为std::exception:
圣