为什么在有可用的右值构造函数时从右值调用 class 引用构造函数重载?

Why does the class reference constructor overload get called from an rvalue when there's an rvalue constructor available?

这个代码

#include <iostream>

struct A
{
    A(int i) {std::cout << "int received\n";}
    A(A& a) {std::cout << "ref received\n";}
};

int main()
{
    int j = 5;
    A a = j;
}

意外抛出以下编译器错误:

error: invalid initialization of non-const reference of type 'A&' from an rvalue of type 'A'
note:   initializing argument 1 of 'A::A(A&)'
note:   after user-defined conversion: A::A(int)

当我删除第二个构造函数重载 A(A& a) 时,一切都按预期进行。我想编译器错误地调用了第二个构造函数而不是第一个构造函数。

为什么会这样?

如何让 class 同时具有引用构造函数和右值构造函数协调工作?

我使用 GNU GCC。

注意:我还注意到一些奇怪的事情:显然,如果我将行 A a = j; 替换为 A a(j);,一切都会按预期进行。然而,这并不令人满意,因为如果我试图从函数参数初始化对象(例如:用 f(j) 调用 void f(A a)),它仍然不起作用。

A a = j; 执行 copy initialization.

直到 C++17,

If T is a class type, and the cv-unqualified version of the type of other is not T or derived from T, or if T is non-class type, but the type of other is a class type, user-defined conversion sequences that can convert from the type of other to T (or to a type derived from T if T is a class type and a conversion function is available) are examined and the best one is selected through overload resolution. The result of the conversion, which is a prvalue temporary (until C++17) prvalue expression (since C++17) if a converting constructor was used, is then used to direct-initialize the object. The last step is usually optimized out and the result of the conversion is constructed directly in the memory allocated for the target object, but the appropriate constructor (move or copy) is required to be accessible even though it's not used. (until C++17)

Class A 有一个复制构造函数将左值引用取为非常量,它不能绑定到从 int 转换而来的临时值。即使来自临时 A 的构造可能会被优化,复制构造函数也必须可用。

使复制构造函数采用左值引用 const(或添加移动构造函数)可以解决问题。

自从 C++17 因为 mandatory copy elision,代码可以正常工作。

The objects are constructed directly into the storage where they would otherwise be copied/moved to. The copy/move constructors need not be present or accessible:

LIVE

另一方面,A a(j);执行direct initializationa直接从j初始化,拷贝构造函数不参与。