为什么在返回从函数的返回类型派生的类型的本地对象时不选择移动构造函数?

以下代码被 Clang 和 GCC(主干版本):

The following code is rejected by both Clang and GCC (trunk versions):

#include <memory>

struct Base 
{
    Base() = default; 
    Base(Base const&) = delete;
    Base(Base&&) = default;
};

struct Derived : Base
{
    Derived() = default; 
    Derived(Derived const&) = delete;
    Derived(Derived&&) = default;
};    

auto foo()
    -> Base
{
    Derived d;    
    return d;   // ERROR HERE
}

导致以下错误:

prog.cc: In function 'Base foo()': prog.cc:21:12: error: use of deleted function 'Base::Base(const Base&)'
     return d;
            ^

根据[class.copy]/32:

According to [class.copy]/32:

当满足省略复制/移动操作的条件时,但不满足异常声明,并且要复制的对象由左值指定,或返回语句中的表达式是(可能用括号括起来)id 表达式,它命名具有自动存储持续时间的对象 在最内层封闭函数或 lambda 表达式的主体或参数声明子句中声明,重载分辨率以选择构造函数首先执行复制,就好像对象是由右值指定的一样

When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a return statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue

如果上面的句子被解析为 (复制省略条件满足 && lvalue) ||(id-expression 指定自动对象),如 这个 CWG 缺陷 似乎表明,为什么最后一个条件在这里不适用?Clang 和 GCC 中是否存在编译器错误?

If the sentence above is meant to be parsed as (copy elision criteria met && lvalue) || (id-expression designating an automatic object), as this CWG defect seems to indicate, why isn't the last condition applying here? Is there a compiler bug both in Clang and GCC?

另一方面,如果句子被解析为 (复制省略标准满足 && (lvalue || id-expression 指定一个自动对象)),不是这是一个非常具有误导性的措辞,值得 DR?

On the other hand, if the sentence is meant to be parsed as (copy elision criteria met && (lvalue || id-expression designating an automatic object)), isn't this a very misleading wording worth a DR?

推荐答案

[class.copy]/32 继续:

[class.copy]/32 continues:

[...] 如果所选构造函数的第一个参数的类型不是对对象类型的右值引用(可能cv 限定),再次执行重载决议,考虑到对象作为左值.

[...] if the type of the first parameter of the selected constructor is not an rvalue reference to the object's type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue.

第一个重载决议,将 d 视为右值,选择 Base::Base(Base&&).但是,所选构造函数的第一个参数的类型不是 Derived&& 而是 Base&&,因此该重载决议的结果将被丢弃,您再次执行重载解析,将 d 视为左值.第二个重载决议选择已删除的复制构造函数.

The first overload resolution, treating d as an rvalue, selects Base::Base(Base&&). The type of the first parameter of the selected constructor is, however, not Derived&& but Base&&, so the result of that overload resolution is discarded and you perform overload resolution again, treating d as an lvalue. That second overload resolution selects the deleted copy constructor.

相关文章