Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

template constructor cannot be selected? [duplicate]

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?

like image 544
goodbyeera Avatar asked Mar 04 '14 06:03

goodbyeera


1 Answers

Note; gcc and clang shows the correct behavior when trying to compile the provided snippet.


Why is vc++ wrong to accept the snippet?

  1. 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&, or const 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.

  2. 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.

like image 101
Filip Roséen - refp Avatar answered Nov 15 '22 03:11

Filip Roséen - refp