template <typename T>
class A {
public:
template<class C> A(const A<C>&) {}
A(A&&) {}
};
void f(A<int>& a)
{
A<int> b(a);
}
The above code doesn't compile in g++/clang++ reporting copy constructor is deleted because of user-provided move constructor (though vc++ compiles OK). Is there any standard requirement that prevents a template constructor from being selected during overload resolution (I know it's not a copy constructor though)? Or is there a requirement that when initializer has the same type as the initializee, it has to select a copy constructor?
Note; gcc
and clang
shows the correct behavior when trying to compile the provided snippet.
vc++
wrong to accept the snippet?A function template can never be instantiated in a way that produces a copy-constructor, as can be read in the two following quotes from the c++ standard:
[class.copy]
2) A non-template constructor for class X is a copy constructor if its first parameter is of type
X&
,const X&
,volatile X&
, orconst volatile X&
and either there are no other parameters or else all other parameters have default arguments (8.3.6)....
6) A declaration of a constructor for class
X
is ill-formed if its first parameter is of type (optionally cv-qualified)X
and either there are no other parameters or else all other parameters have default arguments. A member function template is never instantiated to produce such a constructor signature.
If a move-constructor is explicitly declared the implicitly generated copy-constructor won't be callable, see the following:
[class.copy]
7) If a class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4)
struct Obj {
template<typename T>
Obj (T const&) { }
Obj (Obj&&) { }
};
...
Obj a;
Obj b (a);
Having the previously mentioned quotes from the c++ standard in mind we can easily see that the above definition of Obj
would be semantically equivalent of the below since our explicitly declared move-constructor will make our implicitly declared copy-constructor = delete;
.
struct Obj {
template<typename T>
Obj (T const&) { }
Obj (Obj const&) = delete;
Obj (Obj&&) { }
};
According to the rules of overload resolution deleted functions still participate when trying to find the best match, but if they win the battle of having the best fit the program is ill-formed. This is exactly what is happening in the snippet.
Obj (Obj const&) = delete
is a better fit than template<typename T> Obj (T const&)
, but since the copy-constructor isn't callable the snippet fails to compile.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With