为什么 g++ 允许返回不可复制的类?
Why does g++ allow returning non-copyable class?
我为不可复制的类创建了这个基类:
class non_copyable
{
public:
non_copyable(const non_copyable&) = delete;
non_copyable& operator=(const non_copyable&) = delete;
virtual ~non_copyable() = default;
protected:
non_copyable() = default;
};
然后我创建了这个派生类:
class manager
: public non_copyable
{
public:
manager()
{
}
std::string s;
};
我能够创建类的实例并像这样返回它:
manager get()
{
return manager();
}
我认为这应该是不可能的,因为复制构造函数被删除并且隐式生成的移动构造函数被删除,因为有一个用户定义(已删除)的复制构造函数。
此代码使用 MinGW-64 7.2 编译,但不使用 MSVC 2017 编译,并生成以下消息:
函数"manager::operator=(const manager &) throw()"(隐式声明)不能被引用 - 它是一个已删除的函数
这是编译器的问题,C++设计允许还是我做错了什么?
在 C++17 中,此操作既不需要移动也不需要复制;整个事情都被省略了。
因此,Visual Studio在实现此语言标准时要么是错误的,要么是不完整的。†
一般来说,尽量不要改变C++自己的语义。防止昂贵的东西很好,但防止免费的东西是IMO太过分了。
†根据您的确切版本,这篇博文可能是相关的——它指出他们试图让这个功能工作,但它太坏了,所以他们暂时回滚了它;我不知道是否有更新的版本可以实现它。
在 c++17 中,prvalue 表达式不是逻辑对象。 在 c++14 中,它们在逻辑上是对象(或者更确切地说,它们是创建它们)。
现在,prvalue 表达式是有关如何创建对象的指令。 在某些情况下,这些指令将应用于创建临时或非临时对象。
这更广为人知的是"保证消除"。 但实际上,它只是在许多情况下(不是全部)消除了任何省略的需要。
manager get() {
return manager();
}
在 c++14 中,manager()
是一个创建对象的 prvalue 表达式。 返回值是另一个 prvalue。 从manager()
到get
的返回值的复制或移动可以通过这两个对象合并其标识和生存期来省略。
在 c++17 中,manager()
是一个 prvalue,get()
的返回值也是如此。 您不会"复制"或"移动"有关如何创建对象的指令,对象也不是。 返回只是告诉返回值"这是您需要的说明"。
manager foo = get();
在这里,我们从 prvalue 构建foo
-- 从有关如何制作manager
的说明中。 不创建临时对象;相反,我们只是按照get()
的 prvalue return 的指示构造对象。
在 c++14 中,我们有一个临时管理器对象,可以使用命名对象foo
省略其生存期。
这种省略和"直接"使用 prvalues 的效果在运行时非常相似,但一个涉及逻辑移动或复制构造函数调用,我们稍后会消除,另一个从未有第二个对象开始。
令人惊讶的是,为什么MSVC2017的行为不同,他们对 c++11 的实现仍然不完整(诚然,每年的方式都较小,但我仍然被烧毁),更不用说 c++17 了。
截至 C++17,我们已保证复制省略。这意味着从函数返回值不需要复制或移动。
不幸的是,Visual Studio还不支持这一点:
P0135R1 保证复制省略 否
- 简单可复制与可简单复制
- reinterpret_cast,只读访问,简单的可复制类型,会出什么问题?
- 对于参加可复制和可移动类的访问者来说,应该有多少过载?
- 可变参数宏:无法通过"..."传递非平凡可复制类型的对象
- 为什么 std::atomic<std::string> 会给出微不足道的可复制错误?
- 我可以隐式地创建一个琐碎的可复制类型吗
- 是std::memcpy在不同的可复制类型之间的未定义行为
- 为什么一对常量是微不足道的可复制的,而对不是?
- 在一个微不足道的可复制结构中,移动语义应该实现吗?
- C 标准:通过复制返回以初始化无RVO的参考:是否有任何副本
- 防止作用域枚举可复制/可移动
- C :对象上的可复制视图
- 防御性地应用 std::move 到平凡可复制的类型是否不可取
- 为什么 std::function 本身是可复制构造的类型?
- 允许移动和复制返回的班级成员
- C++不可复制的 lambda 的行为是可复制的
- 错误:无法通过'...'传递非平凡可复制类型的对象'class boost::filesystem::path'
- 不能让类是微不足道的可复制的。我做错了什么?
- 使用临时存储区复制普通的可复制类型:允许吗
- 如何返回一个不可移动(但可复制)的对象