GCC: template constructor instantiated when copy-constructor needed

c++, copy-constructor, gcc

Solution

gcc is correct.

There are a couple of issues here which unfortunately have become conflated in your question:

First, the behavior of gcc < 4.7 is not fundamentally different; all versions of gcc since (at least) 4.4 reject the very similar program:

struct S;

template<typename, typename> struct U {};
template<typename T> struct U<S, T> {};
template<typename T> struct U<T, S> {};

struct S {
  S() = default;
  template<typename T, typename = typename U<S, T>::type> S(T) {}
};

int main() {   
  S a;
  S b(a);
}

Note that the only real difference is that the copy-initialization is explicit rather than contained in a function call. Clang accepts this program, by the way.

Next, it's not fundamental to this issue that the copy constructor be involved (rule 12.8p6 in C++11); here's another similar program that gcc (all versions) rejects and clang accepts:

struct S {};

template<typename, typename> struct U {};
template<typename T> struct U<S, T> {};
template<typename T> struct U<T, S> {};

void f(S);
template<typename T> typename U<S, T>::type f(T);

int main() {   
  S a;
  f(a);
}

The difference between clang and gcc is in the application of 14.8.2p8:

[...] [ Note: The evaluation of the substituted types and expressions can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note ]

The ambiguity in the template specialization `ambiguous<int, int>` is outside the immediate context, so the program is ill-formed. (A supporting argument for this is that template specialization ambiguity does not appear in the succeeding list of reasons for type deduction to fail).

MSVC is different again; it accepts the following program that both clang and gcc reject:

template<typename T> struct U { typedef typename T::type type; };
struct S {
    S() = default;
    template<typename T, typename = typename U<T>::type> S(T) {}
};

int main() {
    S a;
    S b(a);
}

This is down to rule 12.8p6:

A declaration of a constructor for a 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.

However, in order to determine whether a member function template instantiation is a constructor ill-formed with regard to 12.8p6, it is necessary to instantiate its declaration (cf. 14.7.1p9). Note that MSVC rejects the following program, so it isn't even consistent:

template<typename T> struct U { typedef typename T::type type; };
struct S {
    S() = default;
    template<typename T> S(T, typename U<T>::type *p = 0) {}
};

int main() {
    S a;
    S b(a);
}

This has some highly amusing behavior effects; MSVC accepts the following (ill-formed) program:

template<typename T> struct U { typedef typename T::type type; };
struct S {
    S() = default;
    template<typename T, typename = typename U<T>::type> S(T) {}
};
template<typename T> typename U<T>::type f(T) { return 0; }

int main() {
    S a;
    S b(a);  // XXX
    f(a);
}

However if the copy-initialization `S b(a)` is commented out, the program is rejected!

Problem

In the following example, `GCC >= 4.7` instantiates the template constructor (which you can observe by reading the error messages) although only the implicitly generated copy-constructor should be needed. ``` #include <type_traits> // 'ambiguous' is ambiguous for 'ambiguous<int, int>' template<typename A, typename B> struct ambiguous : std::false_type {}; template<typename T> struct ambiguous<int, T> : std::true_type {}; template<typename T> struct ambiguous<T, int> : std::true_type {}; // quantity template<typename Type> class quantity { public: quantity() = default; // Copy-constructor is implicitly created // Template constructor template< typename T, typename = typename std::enable_if<ambiguous<Type, T>::value>::type > quantity(quantity<T>) {} template< typename T, typename = typename std::enable_if<ambiguous<Type, T>::value>::type > void set(quantity<T>) {} }; // main int main() { quantity<int> a; quantity<float> b; b.set(a); } ``` The above code compiles in `GCC < 4.7`, `clang` and `MSVS` (don't know which version, I used the one from http://rextester.com/runcode). In `GCC >= 4.7` compilation fails with the following message: ``` main.cpp: In substitution of ‘template<class T, class> quantity<Type>::quantity(quantity<T>) [with T = int; <template-parameter-1-2> = <missing>]’: main.cpp:39:12: required from here main.cpp:23:9: error: ambiguous class template instantiation for ‘struct ambiguous<int, int>’ typename = typename std::enable_if<ambiguous<Type, T>::value>::type ^ main.cpp:9:8: error: candidates are: struct ambiguous<int, T> struct ambiguous<int, T> : std::true_type {}; ^ main.cpp:12:8: error: struct ambiguous<T, int> struct ambiguous<T, int> : std::true_type {}; ^ main.cpp: In function ‘int main()’: main.cpp:31:10: error: initializing argument 1 of ‘void quantity<Type>::set(quantity<T>) [with T = int; <template-parameter-2-2> = void; Type = float]’ void set(quantity<T>) {} ``` So when invoking `b.set(a);`, `GCC` apparently looks for a copy constructor and on the way instantiates the template constructor which in turn instantiates `ambiguous<int, int>` which is (uhm...) ambiguous. Question: Is `GCC` right to instantiate the template constructor even though a copy constructor is needed?

Original source