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

Lor*_*156 4 c++ g++ visual-c++ c++17

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

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;
};
Run Code Online (Sandbox Code Playgroud)

然后我创建了这个派生类:

class manager
    : public non_copyable
{
public:
    manager()
    {
    }

    std::string s;
};
Run Code Online (Sandbox Code Playgroud)

我能够创建一个类的实例并返回如下:

manager get()
{
    return manager();
}
Run Code Online (Sandbox Code Playgroud)

我认为这是不可能的,因为删除了复制构造函数并删除了隐式生成的移动构造函数,因为存在用户定义(已删除)的复制构造函数.

此代码使用MinGW-64 7.2编译,但不与MSVC 2017编译,并产生以下消息:

函数"manager :: operator =(const manager&)throw()"(隐式声明)无法引用 - 它是一个已删除的函数

这是编译器的问题,C++设计允许还是我做错了什么?

Lig*_*ica 6

在C++ 17中,此操作既不需要移动也不需要复制; 整个事情都被省略了.

因此,Visual Studio在实现此语言标准时要么是错误的,要么是不完整的.

一般来说,尽量不要改变C++自己的语义.防止昂贵的东西是好的,但防止免费的东西是IMO迈出的一步.

根据您的确切版本,此博客文章可能是相关的 - 它声明他们试图使这个功能工作,但它太破碎了,所以他们暂时回滚; 我不知道是否有更新版本实现它.


Yak*_*ont 4

中,纯右值表达式在逻辑上不是对象。在中,从逻辑上讲,它们是对象(或者更确切地说,它们创建了它们)。

现在,纯右值表达式是关于如何创建对象的指令。在某些情况下,这些指令适用于创建临时或非临时对象。

这更普遍地称为“保证省略”。但实际上,它只是在许多情况下(不是全部)消除了任何省略的需要。

manager get() {
  return manager();
}
Run Code Online (Sandbox Code Playgroud)

manager()中是创建对象的纯右值表达式。返回值是另一个纯右值。可以通过合并其身份和生命周期的这两个对象来消除manager()从到 返回值的复制或移动。get

manager()中是纯右值,就像 的返回值一样get()。您不需要“复制”或“移动”有关如何创建对象的说明,对象也不是。return 只是告诉返回值“这是您需要的说明”。

manager foo = get();
Run Code Online (Sandbox Code Playgroud)

在这里,我们从纯右值构造foo——从如何创建manager. 没有创建临时对象;相反,我们只是按照 的纯右值返回的指示构造对象get()

中,我们有一个临时管理器对象,其生命周期可以用命名对象来消除foo

这种省略和“直接”使用纯右值在运行时的效果非常相似,但一个涉及我们稍后消除的逻辑移动或复制构造函数调用,另一个从一开始就没有第二个对象。

至于为什么 MSVC2017 的行为有所不同,他们对的实现仍然不完整(无可否认,每年都以较小的方式实现,但我仍然被烧伤),更不用说了。