Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

While doing copy-elision, the compiler doesn't consider the copy constructor in overload resolution, when the move constructor is deleted. Why?

I can understand the compiler is doing copy-elision in the code below, as the copy and move constructors are not invoked in the so called copy-initialization done in main(). See live example.

#include <iostream>
struct S {
    S() = default;
    S(const S&) { std::cout << "copy ctor" << '\n'; }
    S(S&&) { std::cout << "move ctor" << '\n'; }
};

int main() {
    S s = S(); 
}

But I can't understand why the code doesn't compile when I delete the move constructor as below:

#include <iostream>
struct S {
    S() = default;
    S(const S&) { std::cout << "copy ctor" << '\n'; }
    S(S&&) = delete;
};

int main() {
    S s = S(); 
}

I can't find anything in §12.8/32 (N4140) that could disallow the copy constructor from being used or elided, in this case. This is the sentence that called my attention in §12.8/32, which seems to indicate that the copy constructor should have been considered in the overload resolution:

If the first overload resolution fails or was not performed, or 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.

Edit

From one of the comments by T.C. below, I understand that when the object to be copied is designated by an rvalue, the compiler, according to §12.8/32, doesn't consider the copy-constructor as a candidate for the copy, even though the copy would be elided anyway. That is, the end result would be the construction of the object s with the default constructor. Instead, in this situation the Standard mandates (where??) the code to be ill-formed. Unless my understanding of this scheme is completely wrong, that doesn't make any sense to me.

like image 460
François-Marie Arouet Avatar asked Aug 02 '15 19:08

François-Marie Arouet


1 Answers

This is nothing specific to do with copy elision or constructors; it is just overload resolution.

If we have a pair of overloads:

void f( T&& rv );
void f( const T& lv );

then the overload resolution rules say that f( T{} ) is a better match for f(T&&).

Copy elision can elide a copy or move, but only when the code is well-defined ( even if the compiler chooses not to implement copy elision). Your code is not well-defined, because it specifies to call a deleted function.

like image 100
M.M Avatar answered Oct 02 '22 23:10

M.M