部分模板专业化和icc

Nir*_*man 6 c++ templates c++11

请考虑以下代码:

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;
}
Run Code Online (Sandbox Code Playgroud)

当我使用icc 16.0.1编译它时,我收到以下消息:

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();
Run Code Online (Sandbox Code Playgroud)

使用clang 3.7.1和gcc 5.3.0编译(并打印"instantiated special").这是icc中的错误,还是我的代码不正确?对我来说,似乎很清楚,第二次专业化比第一次专业化更严格; 它与锁定第一个模板参数的事实相同.

编辑:我应该补充:如果这是icc中的错误,是否有一个很好的解决方法?

Bar*_*rry 4

是的,这是 ICC 的一个错误。


两个部分专业化都与您的实现相匹配,因此请了解两个合成函数的部分模板排序规则:

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> );
Run Code Online (Sandbox Code Playgroud)

部分排序规则涉及为每个模板参数合成新类型,并尝试对其余的每个重载进行推导。首先,我们尝试U对进行推论Foo<_U1, _U2, std::integral_constant<int, _U2::value>>。这会失败,因为_U1不匹配double。因此,第一个重载至少不如第二个重载那么专业。接下来,我们尝试推论T和。使用和即可成功。所以第二个重载至少和第一个重载一样专业。UFoo<double, _U3, std::integral_constant<int, _U3::value>>T=doubleU=_U3

因此,第二个重载比第一个重载更加专门。有一种独特的最专业的部分偏化,它应该被实例化(并且由 gcc 和 clang 实例化)。ICC 未能这样做是一个错误。