部分模板特化和icc

6

考虑以下代码:

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;
}

当我使用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();

使用clang 3.7.1和gcc 5.3.0可以编译此代码(并打印“实例化特殊”)。这是icc的一个错误吗,还是我的代码有误?对我来说,第二个专门化比第一个严格更加专业化;它与第一个完全相同,除了锁定第一个模板参数。

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


我觉得这明显是icc的一个bug。 - Cameron
请问您能否检查一下下面的评论中的建议是否可以解决ICC上的问题?当然,这并不是完全解决您的问题,只是在调查中向前迈进了一步。 - bogdan
在实际代码中,整数来自U的嵌套成员,这反过来导致clang出现问题,因此我的同事从非类型转移到类型模板参数。 - Nir Friedman
1个回答

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> );

部分排序规则涉及为每个模板参数合成新类型,并尝试针对其余重载进行推导。首先,我们尝试将UFoo<_U1, _U2, std::integral_constant<int, _U2::value>>进行推导。由于_U1double不匹配,因此失败。因此,第一个重载不至少比第二个重载更专业。接下来,我们尝试将TUFoo<double, _U3, std::integral_constant<int, _U3::value>>进行推导。这成功了,T=doubleU=_U3。因此,第二个重载至少与第一个重载一样专业。

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


部分排序和非推断上下文再次发挥作用... 用一个推导出的 int I 替换 U::value 很可能会使事情按预期工作。看起来EDG前端(这里最有可能是ICC使用的)与MSVC一致,并且由于非推导上下文,两者都没有至少作为另一个的专门化重载。令人担忧的是,我已经在其 --strict 模式下测试了一个非MSVC-quirks EDG,它仍然做同样的事情。在编译器方面是2对2。太好了。 - bogdan
@bogdan 为什么会导致扣除失败? - Barry
我同意在你的解释中,推导在第二步失败是没有意义的。我只是指出未被推导的上下文很可能是问题的原因(我对EDG的行为感到非常惊讶)。我会要求OP检查是否将U::value更改为0(或按上面建议的)可以解决ICC上的问题。 - bogdan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接