简体   繁体   中英

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

This code

#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;
}

unexpectedly throws the following compiler error:

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)

When I remove the second constructor overload A(A& a) , everything works as expected. I suppose that the compiler mistakenly calls the second constructor instead of the first one.

Why does this happen?

How can I have a class with both a reference constructor and an rvalue constructor work in harmony?

I use GNU GCC.

Note: I also noticed something weird: Apparently, if I replace the line A a = j; with A a(j); , everything works as expected. This however is not satisfactory because it still doesn't work if I'm trying to initialize the object from a function argument (ex: calling void f(A a) with f(j) ).

A a = j; performs copy initialization .

Until 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 has a copy construtor taking lvalue-reference to non-const, which can't bind to the temporary converted from int . Even the construction from the temporary A might be optimized the copy constructor has to be available.

Making the copy constructor taking lvalue-reference to const (or adding move constructor) would solve the issue.

Since C++17 because of mandatory copy elision , the code would work fine.

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

On the other hand, A a(j); performs direct initialization , a is initialized from j directly, copy constructor doesn't involve in.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM