引发从不可复制派生的可复制类
Throwing copyable class deriving from noncopyable
我有一个框架,它将异常定义为不可复制类,我们从中派生出一个可复制类(定义一个调用非复制基类构造函数的复制构造函数)
这适用于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在函数foo()的末尾抱怨Error 1 error C2280: 'u::u(const u &)' : attempting to reference a deleted function
。
g++和clang都编译代码,它们根本不使用复制构造函数(e对象被移动),但如果e
不可复制构造,它们都不会编译。
编辑:我已经编辑了代码以强制复制。
顺便说一句,如果u
复制函数没有被删除(也没有定义,在c++11之前是不可复制的),则MSVC在u::u(const u&)
查找期间的链接阶段失败。(未解决的外部)
我的代码中有缺陷吗,还是MSVC中有这个错误?
首先,e
和u
都有用户声明的复制构造函数。这意味着它们没有隐式生成的移动构造函数或移动赋值运算符。(因此,您的声明"e
对象已移动"是错误的;下面将对此进行详细说明)。
e
是可复制的,u
是不可复制的。e
被认为是可移动的,因为如果没有移动构造函数,移动就回到了复制。
根据C++14的[except.show]部分(与C++11中相同),抛出异常时对象的初始化等效于:
e temp = e_; // (1)
const e& _e = temp; // (2)
对于(1),不创建临时,因为e_
和temp
具有相同的类型。
对于(2)是直接绑定:_e
直接引用temp
,不存在临时性。
您可能还记得(1)是一个复制省略上下文。这意味着必须存在一个可访问的复制或移动构造函数;但编译器可以跳过对该构造函数的调用,并为两个对象使用相同的内存空间。
这可能就是你所说的"e
对象被移动了"的意思:你看到它没有被复制,但假设它移动了,而事实上它是复制省略。
把所有这些放在一起:throw
-catch
工作的唯一要求是e
有一个可调用的移动构造函数或复制构造函数。(这个构造函数实际上可能没有被调用,但它必须存在)。
事实上,您的e
确实有一个可调用的副本构造函数,因此代码是正确的,并且您尝试的MSVC版本存在错误。
如果您仍然可以访问该版本,那么尝试实际编写e temp = e_; const e& _e = temp;
并查看该代码是否生成相同的错误消息会很有趣。这将显示错误是否与编译器对复制初始化的实现有关,而不是与抛出有关。
MSVC 2013确实需要可复制的异常类。此外,它的构造函数必须是显式的。以下代码对于MSVC 2013不正确:
class Ex {
public:
Ex(){}
private:
explicit Ex(const Ex&);
};
int main()
{
throw Ex(); // error
}
你似乎无法以适当的方式处理这个问题;指针的任何魔术都会导致不正确的对象删除。
我只看到一个解决方案:将您的问题通知框架开发人员。
- 简单可复制与可简单复制
- reinterpret_cast,只读访问,简单的可复制类型,会出什么问题?
- 对于参加可复制和可移动类的访问者来说,应该有多少过载?
- 有没有办法复制派生类指针的向量而不将其强制转换为基类?
- 可变参数宏:无法通过"..."传递非平凡可复制类型的对象
- 为什么 std::atomic<std::string> 会给出微不足道的可复制错误?
- 我可以隐式地创建一个琐碎的可复制类型吗
- 是std::memcpy在不同的可复制类型之间的未定义行为
- 从不可复制派生 AbstractBaseClasses (ABC) 是否有缺点?
- 为什么一对常量是微不足道的可复制的,而对不是?
- 在一个微不足道的可复制结构中,移动语义应该实现吗?
- 复制派生类更改基类的构造函数
- 防止作用域枚举可复制/可移动
- C :对象上的可复制视图
- 复制派生类的分配运算符
- 防御性地应用 std::move 到平凡可复制的类型是否不可取
- 为什么 std::function 本身是可复制构造的类型?
- C++不可复制的 lambda 的行为是可复制的
- 错误:无法通过'...'传递非平凡可复制类型的对象'class boost::filesystem::path'
- 引发从不可复制派生的可复制类