Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partial Template specialization and icc

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?

like image 436
Nir Friedman Avatar asked May 13 '16 17:05

Nir Friedman


1 Answers

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.

like image 172
Barry Avatar answered Oct 20 '22 15:10

Barry