【问题标题】:Compiler variance for the type of deduced (non-type) template parameter推导(非类型)模板参数类型的编译器差异
【发布时间】:2021-04-23 00:27:40
【问题描述】:

考虑以下示例:

#include <type_traits>

struct A {};

template <A const i> void f() { 
    static_assert(std::is_same_v<decltype(i), A const>);  // #1
    A& ar = i;                                            // #2
}

int main() {    
    f<A{}>();
}

Clang(1) 和 GCC(1) 都拒绝,并出现以下两个看似冲突的错误:

#1 error: static_assert failed due to requirement 'std::is_same_v<A, const A>'
#2 error: binding reference of type 'A' to value of type 'const A' drops 'const' qualifier

GCC demo, Clang demo.

另外,如果我们将非类型模板参数的类型改为占位符类型,如下:

template <auto const i> void g() { 
    static_assert(std::is_same_v<decltype(i), A const>);  // #1
    A& ar = i;                                            // #2
}

然后 GCC 接受 #1,而 Clang 拒绝它(都拒绝 #2 如上所述)。

GCC demo, Clang demo.

这里发生了什么,哪个编译器是正确的(如果有的话)?


(1) GCC HEAD 11.0.0 20210117 和 Clang HEAD 12.0.0 (20210118),-std=c++20.

【问题讨论】:

  • 如果你也能链接问题的godbolt示例,那就太好了

标签: c++ templates gcc clang language-lawyer


【解决方案1】:

GCC 和 Clang 都(可能)正确地为 #1#2 发出错误 f

根据[temp.param]/6 [强调我的]:

非类型模板参数应具有以下之一 (可能是 cv 限定的)类型:

  • (6.1) 结构类型(见下文),
  • (6.2) 包含占位符类型 ([dcl.spec.auto]) 的类型,或
  • (6.3) 推导类类型 ([dcl.type.class.deduct]) 的占位符。

模板参数上的顶级 cv 限定符 被忽略 在确定其类型时。

顶级 cv 限定符在确定其类型时被忽略;与处理需要推导的占位符类型的 (6.2) 和 (6.3) 相比,对于 (6.1) 的“确定”指的是什么可以说有点模糊。

[dcl.type.decltype]/1 [强调我的]:

对于表达式Edecltype(E) 表示的类型定义为 如下:

  • [...]
  • 否则,如果E 是一个无括号的id 表达式,命名一个非类型模板参数,decltype(E) 是 执行任何必要的类型推导后的模板参数 ([dcl.spec.auto], [dcl.type.class.deduct]);
  • [...]

提到fdecltype(i)的类型是非类型模板参数的类型,在进行了any必要的类型推导后,后跟两个引用连接[temp.参数]/6.2 和 [temp.param]/6.3。

因此,f 中的#1 的关键是 [temp.param]/6.1 是否也经历“确定其类型”,即使它被明确注释为 A const(无需推断)。 GCC 和 Clang 似乎都同意这种情况:A const 经历“类型确定”,const 被删除。


f 中理解#2 更直接;与非类型模板参数(类类型)A 关联的实际 模板参数对象 不一定与非类型模板参数类型相同;相反,它的类型由[temp.param]/8 [emphasis mine] 管理:

一个id-expression命名一个非类型模板参数类类型T表示一个静态存储持续时间对象类型const T,称为模板参数对象,其值是对应模板参数转换为模板参数类型后的值。相同类型的程序中所有具有相同值的模板参数表示相同的模板参数对象。模板参数对象应具有常量破坏([expr.const])。 [ 注意: 如果一个 id 表达式命名一个非类型非引用模板参数,那么如果它具有非类类型,它就是一个纯右值。否则,如果它是类类型 T,它是一个左值并且具有类型 const T ([expr.prim.id.unqual])。 — 尾注 ]

使得任何类类型T模板参数对象 始终具有const T 类型,从而解释#2 处的错误。请注意,后者仅适用于类类型,因为命名非类型非引用模板参数的 id 表达式是纯右值。


至于g#1 的GCC 和Clang 之间的差异,在应用[temp.param]/6,特别是[temp.param]/6.2 时没有含糊之处,在忽略cv 的情况下应用类型推导- 模板参数上的限定符。意思是decltype(i)g 中是A,而GCC 错误地接受#1 行。我已相应地提交了以下 GCC 错误报告:

【讨论】:

    猜你喜欢
    • 2011-10-07
    • 1970-01-01
    • 1970-01-01
    • 2018-05-01
    • 2021-12-30
    • 2021-02-18
    • 2015-10-23
    相关资源
    最近更新 更多