为什么 g++ 允许返回不可复制的类?

Why does g++ allow returning non-copyable class?

本文关键字:可复制 返回 g++ 许返回 为什么      更新时间:2023-10-16

我为不可复制的类创建了这个基类:

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 保证复制省略 否