当前位置: 首页 > 知识库问答 >
问题:

推导(非类型)模板参数类型的编译器方差

牧梓
2023-03-14

考虑以下示例:

#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{}>();
}
#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

而且,如果我们将非类型模板参数的类型改为占位符类型,如下所示:

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,如上)。

海合会演示,铿锵演示。

(1)GCC HEAD 11.0.0 202 10117和Clang HEAD 12.0.0(20210118),-std=C++20

共有1个答案

阮俊弼
2023-03-14

对于f#1#2发出错误的GCC和Clang(可能)都是正确的。

按照[temp.param]/6[强调我的]:

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

    null

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

对于表达式E,由DeclType(E)表示的类型定义如下:

  • [...]
  • 否则,如果E是命名非类型模板参数的未加括号的ID表达式,则DeclType(E)在执行任何必要的类型演绎([dcl.spec.auto],[dcl.type.class.dectut])后是模板参数的类型;
  • [...]

提到fDeclType(i)的类型在html" target="_blank">执行任何必要的类型推导后是非类型模板参数的类型,然后是连接到[temp.param]/6.2和[temp.param]/6.3的两个引用。

因此,F#1的关键在于[temp.param]/6.1是否也经历了“确定其类型”,即使它被明确地注释为a const(不需要推导)。GCC和Clang似乎都同意这种情况:常量经历“确定类型”,而常量被删除。

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

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

以便任何类类型t的模板参数对象总是具有constt类型,从而解释#2中的错误。请注意,后者仅适用于类类型,因为命名非类型非引用template_parameter的id表达式是PRVALUE。

至于在G#1处的GCC和Clang之间的差异,在应用[temp.param]/6特别是[temp.param]/6.2时并不含糊,在应用类型推断的同时忽略template-parameter上的cv-qualifiers。意思是Decltype(i)G中的A,并且GCC接受#1行是错误的。我已经相应地提交了以下GCC bug报告:

  • 错误98820-占位符(自动)非类型模板参数错误地推导为类类型参数的“const”
 类似资料:
  • OpenGL定义了C函数来管理资源。我编写了一个简单的包装器来以RAII的方式处理它们。函数对类似于和。但是,也有一些函数对适用于资源数组,例如和。对于前者,我编写了一个简单的类来完成这项工作,对于后者,我编写了另一个处理数组的类。然而,我注意到有时我只使用一个缓冲区或纹理,在那里我不必承担向量的费用,我想如果发布函数在开始时采用大小参数,我会专门化类析构函数,但是... 对于上述SSCCE g树

  • 使用以下代码生成时 生成以下诊断(代码后): 诊断: 有趣的部分不是关于歧义错误本身(这不是这里主要关注的问题)。有趣的是,当仅使用函数名调用fun时,第一个fun的模板参数F被解析为纯函数类型double(double),而第二个fun的模板参数F被解析为更期望的函数指针类型。 然而,当我们将调用<代码>乐趣(测试) 更改为<代码>乐趣( 这种行为似乎是所有Clang和GCC(以及Visual

  • 我发现了和类型推导的以下行为,这对我来说是意想不到的: 中的两行都会导致错误: 没有函数模板“stdfunc_test”的实例与参数列表匹配 尝试在Visual Studio 2015中编译时。 为什么类型演绎不从函数类型中演绎模板类型,有没有变通方法?

  • 也许我累了,但是我被这个简单的局部特化卡住了,它不起作用,因为指定模板参数: 将替换为,或没有帮助。那么这种专门化是否可能呢?

  • 问题内容: 以下代码在t3行中出现编译错误: 错误消息是: 类型不匹配:无法从对象转换为T 我知道我可以使用强制转换或手动绑定来解决问题,我的问题是: 编译器进行自动绑定是否如此困难,是否会失败? 编辑:添加了错误消息。 编辑:添加了另一个示例如何不会发生该错误。 编辑:删除了第二个示例,因为它令人困惑,使问题更加清楚。 问题答案: 在第一种情况下,您有两个具有名为的类型参数的泛型方法,但是这些类