Consider the following code:
template <class T, class U, class V>
struct Foo { };
template <class T, class U>
struct Foo<T, U, std::integral_constant<int, U::value>> {
static void print()
{
std::cerr << "instantiated";
}
};
template <class U>
struct Foo<double, U, std::integral_constant<int, U::value>> {
static void print()
{
std::cerr << "instantiated special";
}
};
struct Bar {
static const int value = 0;
};
int main(int argc, char ** argv)
{
using Baz = Foo<double, Bar, std::integral_constant<int, 0>>;
Baz::print();
return 0;
}
When I compile this with icc 16.0.1, I get the following message:
main.cpp(38): error: more than one partial specialization matches the template argument list of class "Foo<double, Bar, std::integral_constant<int, 0>>"
"Foo<T, U, std::integral_constant<int, U::value>>"
"Foo<double, U, std::integral_constant<int, U::value>>"
Baz::print();
With clang 3.7.1 and gcc 5.3.0 this compiles (and "instantiated special" is printed). Is this a bug in icc, or is my code incorrect? To me it seems clear that the second specialization is strictly more specialized than the first; it is identical to the first other than the fact that it locks down the first template parameter.
Edit: I should add: if this is a bug in icc, is there a good workaround?
Yes, this is a bug in ICC.
Both partial specializations match your implementation, so go into the partial template ordering rules on two synthesized functions:
template <class T, class U> void f(Foo<T, U, std::integral_constant<int, U::value> );
template <class U> void f(Foo<double, U, std::integral_constant<int, U::value> );
The partial ordering rules involve synthesizing new types for each template argument and attempting to do deduction with each overload against the rest. First, we attempt to deduce U
against Foo<_U1, _U2, std::integral_constant<int, _U2::value>>
. This fails, since _U1
doesn't match double
. So the first overload isn't at least as specialized than the second. Next, we attempt to deduce T
and U
against Foo<double, _U3, std::integral_constant<int, _U3::value>>
. This succeeds with T=double
and U=_U3
. So the second overload is at least as specialize as the first.
As a result, the second overload is more specialized than the first. There is a unique most specialized partial partialization, which is the one that should be instantiated (and is by gcc and clang). ICC's failure to do so is a bug.
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