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?