C++ 正在抛出从noncopyable派生的可复制类
我有一个将exception定义为不可复制类的框架,我们从中派生了一个可复制类(定义一个复制构造函数调用一个不可复制基类构造函数) 这在g++下工作,但在MSVC 2013下不起作用 以下代码将重现该问题:C++ 正在抛出从noncopyable派生的可复制类,c++,c++11,visual-c++,C++,C++11,Visual C++,我有一个将exception定义为不可复制类的框架,我们从中派生了一个可复制类(定义一个复制构造函数调用一个不可复制基类构造函数) 这在g++下工作,但在MSVC 2013下不起作用 以下代码将重现该问题: #include <iostream> using namespace std; #if defined _MSC_VER #define __PRETTY_FUNCTION__ __FUNCTION__ #endif class u { u(const u&)
#include <iostream>
using namespace std;
#if defined _MSC_VER
#define __PRETTY_FUNCTION__ __FUNCTION__
#endif
class u {
u(const u&) = delete;
const u& operator=(const u&) = delete;/* the library we use defines it as const u& */
public:
u() { cout << __PRETTY_FUNCTION__ << "def" << endl; }
protected:
explicit u(int i) { cout << __PRETTY_FUNCTION__ << "int: " << i << endl; }
};
class e : public u {
public:
e() { cout << __PRETTY_FUNCTION__ << "def" << endl; }
e(const e& _e) : u(1) { cout << __PRETTY_FUNCTION__ << "cpy" << endl; }
e& operator=(const e& _e) { cout << __PRETTY_FUNCTION__ << endl; return *this; }
};
int foo() {
e _e;
throw _e;
return 0;
}
int main() {
try {
foo();
} catch(const e& _e) {
cout << "in catch e" << endl;
} catch(...) {
cout << "in catch..." << endl;
}
#if defined _MSC_VER
cout << "press enter to exit" << endl;
cin.get();
#endif
return 0;
}
#包括
使用名称空间std;
#如果已定义,则为MSC版本
#定义漂亮函数__
#恩迪夫
u类{
u(常数u&)=删除;
const u&operator=(const u&)=delete;/*我们使用的库将其定义为const u&*/
公众:
u(){coutMSVC 2013确实需要可复制的异常类。此外,其构造函数必须是显式的。以下代码不适用于MSVC 2013:
class Ex {
public:
Ex(){}
private:
explicit Ex(const Ex&);
};
int main()
{
throw Ex(); // error
}
似乎您无法以正确的方式处理此问题;任何指针魔法都会导致不正确的对象删除
我只看到一个解决方案:将您的问题通知框架开发人员。首先,e
和u
都有用户声明的复制构造函数。这意味着它们没有隐式生成的移动构造函数或移动赋值操作符。(因此您声明“移动了e
对象”是错误的;下面将对此进行详细说明)
e
是可复制的,u
是不可复制的。e
被认为是可移动的,因为如果没有移动构造函数,移动会返回到复制
根据C++14的[except.throw]部分(与C++11相同),抛出异常时对象的初始化相当于:
e temp = e_; // (1)
const e& _e = temp; // (2)
对于(1),不会创建临时文件,因为e_u
和temp
具有相同的类型
对于(2)它是直接绑定:\u e
直接引用temp
,不再有临时绑定
您可能还记得(1)是一个上下文。这意味着必须存在一个可访问的复制或移动构造函数;但允许编译器跳过对该构造函数的调用,并为两个对象使用相同的内存空间
这可能就是您所说的“移动了e
对象”的意思:您看到它没有被复制,但假设它是一个移动,而实际上它是复制省略
总而言之:throw
-catch
要工作的唯一要求是e
有一个可调用的移动构造函数或复制构造函数。(这个构造函数实际上可能没有被调用,但它必须存在)
您的e
实际上有一个可调用的复制构造函数,因此代码是正确的,并且您尝试的MSVC版本存在错误
如果您仍然可以访问该版本,那么尝试实际编写e temp=e_;const e&\u e=temp;
并查看该代码是否会生成相同的错误消息将非常有趣。这将显示该错误是否与编译器的副本初始化实现有关,而不是与抛出的错误有关。,因此我将使用MSVC 2015中修复的MSVC错误。看起来像MSVC错误,而不是调用移动构造函数:我的第一个猜测是,当抛出e
对象时,您可能会将其捕捉为u
(而不是作为u
的引用)在这种情况下,您将需要u
的copy-ctor。可能gcc和clang将copy调用放在catch子句附近(此处不存在该子句),但MSVC可能希望将其放在throw
语句之后。它不会被调用,但编译器会引用它。u
是一个异常类,它有效地禁用了C++11异常处理机制。复制构造函数的const u&
返回大声且清楚地表明这不是故意的:t开发人员不称职。因此,简单的解决办法是:抛弃该库。也就是说,MSVC有许多与构造函数相关的bug,但虽然它在法律上不在其权利范围内,但它在道德上是正确的,因为您告诉它,e
是-au
,没有添加任何数据。因此,使继承成为非-code>public
使代码可以编译,但却违背了目的。:(对不起,我喝醉了)我已经修复了我的示例,我仍然可以访问此版本的编译器。e temp=e_;const e&\u e=temp;
代码编译时不会出错。尝试抛出这些变量中的任何一个都会失败。