【问题标题】:Class template partial specialization: compiler error类模板部分特化:编译器错误
【发布时间】:2020-01-30 15:33:32
【问题描述】:

这个程序

#include <iostream>

template <int I>
struct A
{
    A() { std::cout << "A<I>()\n"; }
};

template <int I>
struct A<I + 5>
{
    A() { std::cout << "A<I + 5>()\n"; }
};


int main()
{
    return 0;
}

既不是由 gcc HEAD 10.0.0 20190 也不是由 clang HEAD 10.0.0 编译的。

例如 gcc 编译器出现错误

prog.cc:10:8: error: template argument '(I + 5)' involves template parameter(s)
   10 | struct A<I + 5>
      |        ^~~~~~~~

类模板偏特化有错吗?

【问题讨论】:

  • 我没有得到你期望的结果......你能详细说明你想要完成的事情以及为什么这个(在我看来 100% 合理的)错误让你感到困惑吗?
  • @MaxLanghof 为什么是错误的类模板偏特化?
  • 您可能只专注于 A&lt;numeric_limits&lt;int&gt;::min()&gt;, .., A&lt;numeric_limits&lt;int&gt;::min() + 4&gt;。 :)

标签: c++ class templates c++17 partial-specialization


【解决方案1】:

查看temp.class.spec.match/3,我们有

如果部分特化的模板参数由于其 template-parameter-listtemplate-id 的结构而无法推导出,则程序是错误的-形成。

举个例子

template <int I, int J> struct A {};
template <int I> struct A<I+5, I*2> {};     // error

template <int I> struct A<I, I> {};         // OK

template <int I, int J, int K> struct B {};
template <int I> struct B<I, I*2, 2> {};    // OK

Clang 的错误信息与此相呼应:

error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used

(请参阅 @StoryTeller 的回答,了解为什么此推论在您的代码中失败,我不会在这里重复。)

【讨论】:

    【解决方案2】:

    这不是有效的偏特化(尽管错误可能会更好)。我们没有内联的子句如下:

    [temp.class.spec]

    8 在类模板部分的参数列表中 专业化,以下限制适用:

    • 专业化应比主模板更专业化。

    “专精不是更专精!?”我想你认为。但事实确实如此。确定哪个“更专业”的规则在[temp.class.order] 中描述。它的要点是我们要考虑两个假设的函数模板重载:

    template <int I>
    struct A { /* ... */ };
    
    template<int I>
    void foo(A<I>); //1
    
    template<int I>
    void foo(A<I + 5>); //2
    

    然后我们执行函数模板的重载解析和部分排序。如果#2 获胜,则它更专业,并且您的声明是合法的。如果没有获胜,则声明无效。部分排序是通过制作一些参数并对一个模板与另一个模板进行模板参数推导来完成的。所以假设我们首先比较第一个和第二个(为了简单起见,我将重命名它们,但它们仍然是重载):

    void foo1(A<0>); -> void foo2(A<I + 5>);
    

    这里的论证推演成功了吗?它没有。 I + 5 是非推断上下文:

    [temp.deduct.type]

    未推断的上下文是:

    5.3 - 一个非类型模板参数或一个绑定在其中的数组 子表达式引用模板参数。

    I 引用了一个模板参数,所以I + 5 是一个非推导上下文。因此模板参数推导在这个方向上失败了。

    让我们尝试另一个方向。我们再次编造一个论点

    void foo2(A<1 + 5>); = void foo2(A<6>);  -> void foo1(A<I>); 
    

    这里的演绎显然成功了。所以根据函数模板偏序的规则,foo1foo2 更专业。这意味着我们的主要专业确实比我们的“部分专业化”更加专业化,这使得部分专业化格式不正确。

    【讨论】:

    • 我真的很想投赞成票,因为它有出色的解释和演示(我认为这就是其他投票的目的),但我仍然认为这个问题的答案实际上并不正确。真正的问题是,对于特化,演绎总是 失败,这使得程序明显不正确。在这种情况下,它没有比主模板更专业,但并不是真正的核心问题。
    • @MaxLanghof - 它可能会成功,但程序仍然是错误的。那将是两者都不是更专业的情况(这也将违反第一个引用的条款)。所以这是非常正确的。
    • 当然,如果所讨论的程序不同的话。我想我的意思是解释编译器消息,它清楚地指出了演绎失败的问题,而不是缺乏“专业性”。但我想,如果标准有重叠的原因导致格式不正确,那将是一个有争议的问题,所以请投赞成票:)
    猜你喜欢
    • 2017-10-16
    • 2011-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多