c++ - 抛出派生自不可复制的可复制类

标签 c++ c++11 visual-c++

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

这适用于 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;
}

MSVC 提示 Error 1 error C2280: 'u::u(const u &)' : 试图在函数 foo() 的末尾引用已删除的函数

g++ 和 clang 都编译代码,它们根本不使用复制构造函数(e 对象被移动),但如果 e 不是可复制构造的,它们都不会编译。

编辑:我已编辑代码以强制复制。

顺便说一句,如果 u 复制函数没有被删除(也没有定义,c++11 之前不可复制),MSVC 在 u::u(const u&) 查找。 ( Unresolved external )

是我的代码有缺陷,还是 MSVC 中有这个错误?

最佳答案

首先,eu 都有用户声明的复制构造函数。这意味着它们没有隐式生成的移动构造函数或移动赋值运算符。 (因此,您声称“e 对象已被移动”是错误的;更多内容请参见下文)。

e 是可复制的,u 是不可复制的。 e 被认为是 movable,因为如果没有 move-constructor,移动会退回到复制。


根据C++14的[except.throw]节(C++11也是一样),抛出异常时的对象初始化等价于:

e temp = e_;      // (1)
const e& _e = temp;  // (2)

对于(1),没有创建临时的,因为 e_temp 具有相同的类型。

对于(2)是直接绑定(bind):_e直接引用temp,不再临时。


您可能还记得 (1) 是 copy elision语境。这意味着必须存在可访问的拷贝或移动构造函数;但允许编译器跳过对该构造函数的调用,并为两个对象使用相同的内存空间。

这可能就是您所说的“e 对象已移动”的意思:您看到它没有被复制,但实际上它是复制省略时假设移动。


把它们放在一起:throw - catch 工作的唯一要求是 e 有一个可调用的移动构造函数或复制-构造函数。 (这个构造函数可能实际上没有被调用,但它必须存在)。

您的 e 实际上确实有一个可调用的复制构造函数,因此代码是正确的,并且您尝试的 MSVC 版本存在错误。

如果您仍然可以访问该版本,那么尝试实际编写 e temp = e_; 会很有趣。 const e& _e = temp; 并查看该代码是否生成相同的错误消息。这将显示该错误是否与编译器的 copy-initialization 实现有关,而不是其抛出的错误。

关于c++ - 抛出派生自不可复制的可复制类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29564370/

相关文章:

c++ - D 中 MFC 的 CRuntimeClass 替换

c++ - 通过 QDataStream 从客户端发送到服务器的字节不匹配

c++ - 为什么 Google 风格指南规定系统标题出现在项目标题之前?

c++ - 字符串数组转换为数字并排序

c++ - 将 unique_ptr 从一个集合移动到另一个集合

c++ - 在 std::shared_ptr 中使用 = 运算符时,先前的指针是否被破坏?

c++ - 没有 msvcr100d.dll 的来源!__CrtDumpMemoryLeaks()

c++ - 在 C++ 中创建目录

具有 Fluent 界面的 C++ Builder 模式

c++ - Boost.Coroutine 不使用分段堆栈