C++ 正在抛出从noncopyable派生的可复制类

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&)

我有一个将exception定义为不可复制类的框架,我们从中派生了一个可复制类(定义一个复制构造函数调用一个不可复制基类构造函数)

这在g++下工作,但在MSVC 2013下不起作用

以下代码将重现该问题:

#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
是-a
u
,没有添加任何数据。因此,使继承成为非-code>public
使代码可以编译,但却违背了目的。:(对不起,我喝醉了)我已经修复了我的示例,我仍然可以访问此版本的编译器。
e temp=e_;const e&\u e=temp;
代码编译时不会出错。尝试抛出这些变量中的任何一个都会失败。