Consider the follow code:
#include <iostream>
class Data{
public:
Data() = default;
Data(Data const&) = delete;
Data(int) {
}
};
int main(){
int a = 0;
const std::string& rs = "abc"; // rs refers to temporary copy-initialized from char array
Data const& d_rf = a; // #2 but here can be complied
// accroding to the standard, the reference in #2 is bound to a temporary object, the temporary is copy-initialized from the expression
}
If T1 or T2 is a class type and T1 is not reference-related to T2, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1 T1” by user-defined conversion ([dcl.init], [over.match.copy], [over.match.conv]); the program is ill-formed if the corresponding non-reference copy-initialization would be ill-formed. The result of the call to the conversion function, as described for the non-reference copy-initialization, is then used to direct-initialize the reference. For this direct-initialization, user-defined conversions are not considered
Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversions that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in [over.match.copy], and the best one is chosen through overload resolution ([over.match]). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call is a prvalue of the cv-unqualified version of the destination type whose result object is initialized by the constructor. The call is used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization.
Accroding to the standard, the type of a
is int
, and the type of the initialized reference is Data
, so from int
to Data
, user-defined conversions are considered using the rules for copy-initialization of an object of type “cv1 T1” by user-defined conversion. It means Data const& d_rf = a;
can be translated to Data temporary = a; Data const& d_rf = temporary;
. For Data temporary = a;
, even though copy elision exists , the copy/move constructor must be checked whether it is available, but the copy constructor of class Data
has been deleted, why can it be complied?
Here are some quote of standard
Copy initialization of reference from enseignement
Copy initialization of reference from cppreference
If the reference is an lvalue reference:
If object is an lvalue expression, and its type is T or derived from T, and is equally or less cv-qualified, then the reference is bound to the object identified by the lvalue or to its base class subobject.
If object is an lvalue expression, and its type is implicitly convertible to a type that is either T or derived from T, equally or less cv-qualified, then the non-explicit conversion functions of the source type and its base classes that return lvalue references are considered and the best one is selected by overload resolution. The reference is then bound to the object identified by the lvalue returned by the conversion function (or to its base class subobject)Otherwise, if the reference is either rvalue reference or lvalue reference to const:
If object is an xvalue, a class prvalue, an array prvalue, or a function lvalue type that is either T or derived from T, equally or less cv-qualified, then the reference is bound to the value of the initializer expression or to its base subobject.
If object is a class type expression that can be implicitly converted to an xvalue, a class prvalue, or a function value of type that is either T or derived from T, equally or less cv-qualified, then the reference is bound to the result of the conversion or to its base subobject.
Otherwise, a temporary of type T is constructed and copy-initialized from object. The reference is then bound to this temporary. Copy-initialization rules apply (explicit constructors are not considered).
[example:
const std::string& rs = "abc"; // rs refers to temporary copy-initialized from char array ]
UPDATE:
We consider the code under N337
according to the standard, the value a
's type is int
, and the destination type that the reference refer to is Data
, so the complier needs to generate a temporary of type Data
by copy initialization. There is no doubt here,so we focus on copy initialization. The source type is int
and the destination type is Data
, this situation conforms to :
Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). If the conversion cannot be done or is ambiguous, the initialization is ill-formed. The function selected is called with the initializer expression as its argument; if the function is a constructor, the call initializes a temporary of the cv-unqualified version of the destination type. The temporary is a prvalue. The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized;
NOTE the bold part, it does not mean the value int
directly initializes the temporary by Data::Data(int)
. It means, int
is firstly converted to Data
by Data::Data(int)
, then this result directly initializes the temporary which is the object that is the destination of the copy-initialization here. If we use code to express the bold part, it is just like Data temporary(Data(a))
.
The above rules is here:
— If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. The applicable constructors are enumerated (13.3.1.3), and the best one is chosen through overload resolution (13.3). The constructor so selected is called to initialize the object, with the initializer expression or expression-list as its argument(s). If no constructor applies, or the overload resolution is ambiguous, the initialization is ill-formed.
Please reback to Data temporary(Data(a))
. Obviously, the copy/move constructor is the best match for argument Data(a). However, Data(Data const&) = delete;
, so the copy/move constructor is not available. Why does the complier not report the error?
#1
is valid since C++17 – Jarod42