C++20 NTTP特化

3

在尝试编译以下代码时,gcc/clang和msvc之间存在分歧:

struct foo {
};

// primary template
template<auto>
struct nttp {
    static constexpr int specializaion = 0;
};

// specialization
template<foo f>
struct nttp<f> {
    static constexpr int specializaion = 1;
};

int main() {
  // Does not compile with msvc 19.30
  nttp<5> x{};
}

这里有一个完整的例子。

Msvc 报错了,说从 intfoo 没有可行的转换方式,这显然是正确的,但就我对偏特化规则的理解来看,这应该与此处没有任何关系。

引用cppreference的话:

当实例化类或变量(自C++14起)模板时,如果有部分特化可用,则编译器必须决定是使用主模板还是其中的一个部分特化。

  1. 如果只有一个特化与模板参数匹配,则使用该特化。
  2. 如果有多个特化匹配,则使用偏序规则来确定哪个特化更为特化。如果最专业的特化是唯一的(如果不唯一,则无法编译程序),则使用它。
  3. 如果没有特化匹配,则使用主模板。

我认为在这种情况下,int 没有匹配到任何特化,因此应该选择主模板。

有趣的是,可以通过将特化中的 auto 限定为一个概念(concept)来解决这个问题:

template<class T>
concept Foo = std::is_same_v<T, foo>;

template<auto>
struct nttp {
    static constexpr int specializaion = 0;
};

template<Foo auto f>
struct nttp<f> {
    static constexpr int specializaion = 2;
};

// compiles with all compilers
static_assert(nttp<5>{}.specializaion == 0);

函数模板也能够正常工作:

constexpr int test(auto) {
    return 0;
}

constexpr int test(foo) {
    return 1;
}

// compiles with all compilers
static_assert(test(5) == 0);

长话短说:根据经验和直觉,我认为这是MSVC中的一个错误,但通常情况下可能会涉及UB(未定义行为)。所以问题是:clang/gcc正确、还是MSVC正确,或者甚至全部正确?
1个回答

3

这是开放的MSVC错误报告:

根据[temp.class.spec.match]/2[temp.class.spec.match]/3,您的程序已经符合良好:

/2 如果可以从给定的实际模板参数列表中推导出部分特化的模板参数,并且推导出的模板参数满足部分特化的相关约束(如果有),则该部分特化将与给定的实际模板参数列表匹配。

/3 如果由于其模板参数列表和模板ID的结构而无法推导出部分特化的模板参数,则程序是不良形式的。

/3是作为P0127R2使用auto声明非类型模板参数)的一部分进行特定更新的,该提案重新编写了之前从CWG1315的决议中修订过的措辞。

(在CWG1315之后,在P0123R2之前)/3 每个模板参数都应在非推断上下文之外的模板ID中至少出现一次。

这次重写特别是为了允许对使用auto占位类型声明的非模板参数进行部分特化。


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