I was trying to specialize Expr
:
#include <tuple>
#include <type_traits>
#include <iostream>
template<class Tp, class List>
struct Expr {
Expr(){std::cout << "0"<< std::endl;};
};
//specialization #1
template<class Tp, int...i>
struct Expr<Tp,std::tuple<std::integral_constant<int,i>...>> {
Expr(){std::cout << "1"<< std::endl;};
};
//specialization #2
template<int...i>
struct Expr<double,std::tuple<std::integral_constant<int,i>...>> {
Expr(){std::cout << "2"<< std::endl;};
};
int main() {
typedef std::tuple<std::integral_constant<int,1>> mylist;
Expr<double,mylist> test{};
return 0;
}
However, I got the following compiler errors:
[x86-64 gcc 6.3] error: ambiguous template instantiation for 'struct Expr<double, std::tuple<std::integral_constant<int, 1> > >'
[x86-64 gcc 6.3] error: variable 'Expr<double, std::tuple<std::integral_constant<int, 1> > > test' has initializer but incomplete type
Here, especially the first error bothers me. I have tried to figure out why this is an ambiguous instantiation.
Shouldn't specialization #2
be chosen by the compiler?
If I avoid wrapping the non-type parameter pack int...i
in std::integral_constant
it compiles without any problem and the second specialization is chosen. The following example works:
#include <tuple>
#include <type_traits>
#include <iostream>
template<class Tp, class List>
struct Expr {
Expr(){std::cout << "0"<< std::endl;};
};
//specialization #1
template<class Tp, class...args>
struct Expr<Tp,std::tuple<args...>> {
Expr(){std::cout << "1"<< std::endl;};
};
//specialization #2
template<class...args>
struct Expr<double,std::tuple<args...>> {
Expr(){std::cout << "2"<< std::endl;};
};
int main() {
typedef std::tuple<std::integral_constant<int,1>> mylist;
Expr<double,mylist> test{};
return 0;
}
That can't be right. This is a gcc bug (I can't find a bug report to it, maybe it hasn't been reported yet?).
You are right, specialization #2 has to be chosen. Because there are 2 specializations that match, partial ordering selects the most specialized one, which in your case is #2 (double
as first parameter is more specialized than any type as first parameter).
Additionally, clang compiles your code without any problems.