There is a paragraph about guaranteed copy elision in c++ draft n4606 [dcl.init] 17.6:
- If the destination type is a (possibly cv-qualified) class type:
- If the initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination, the initializer expression is used to initialize the destination object. [ Example:
T x = T(T(T()));
calls theT
default constructor to initializex
. — end example ]- [...]
There is also a Q&A talks about how it works.
To myself understanding, the rule I quoted guarantees that no ctors should get involved when initializer expression is a prvalue and the cv-unqualified version of the source type is the same class as the class of the destination. So that no needs to check the existence of copy or move ctor, which makes following codes to be legal in C++17:
struct A {
A() {}
A(A const &) = delete;
A(A &&) = delete;
};
A f() { return A(); } // it's illegal in C++14, and suppose to be legal in C++17
However, what drives me crazy is I can't find similar rules in list-initialization section in c++ draft n4606. What I found is ([dcl.init.list] 3.6)
[...]
- Otherwise, if
T
is a class type, constructors are considered. The applicable constructors are enumerated and the best one is chosen through overload resolution (13.3, 13.3.1.7). If a narrowing conversion (see below) is required to convert any of the arguments, the program is ill-formed. [...]
Since list-initialization has higher priority than the first rule I quoted, we should consider the rule in list-initialized section when the initializer is a initializer-list. As we can see, constructors are considered when list-initialize a class type T
. So, continued to the previous example, will
A ff() { return {A()}; }
be legal in C++17? And can someone find where the standard draft specify how does guaranteed copy elision work in list-initialization?