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

在C非类型模板参数中使用decltype(auto)

云英才
2023-03-14

我正在学习C 17非类型模板参数的新功能decltype(auto)。我编写了一个简单的代码片段,如下所示:

#include <type_traits>

template<decltype(auto) arg>
struct Foo {};

int
main()
{
    constexpr int x = 42;
    static_assert(std::is_same_v<Foo<42>, Foo<x>>);
}

据我所知,福

但是,static_assert语句使用clang、MSVC 19.27编译,但在GCC 10.2、MSVC 19.25编译时失败。

我的问题是:为什么编译器的行为不同?标准对此有何规定?

链接到编译器资源管理器:

叮当声https://godbolt.org/z/66M695

海湾合作委员会https://godbolt.org/z/3v5Mhd

MSVC 19.25https://godbolt.org/z/qP6v89

MSVC 19.27https://godbolt.org/z/14aK5Y

共有3个答案

蓬思博
2023-03-14

我认为这是一个gcc错误,static_assert应该通过。

据此:

如果模板参数的类型包含占位符类型,则通过占位符类型推断从模板参数的类型确定推断的参数类型。。。

在此上下文中,占位符类型推断意味着参数的类型被推断为由以下声明推断:

decltype(auto) t = 42; // for Foo<42> : int
decltype(auto) t = x;  // for Foo<x> : int const

然后,根据这个:

非类型模板参数应具有以下类型之一(可选cv合格):

...

(4.6)包含占位符类型的类型。

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

由于在根据发明的声明确定类型时忽略了顶级限定符,因此Foo

杜俊风
2023-03-14

在C17中,我认为由于以下规则,GCC是错误的:
temp。类型#1

如果需要,两个模板ID引用相同的类、函数或变量

1.1它们的模板名称、运算符函数ID或文本运算符ID引用同一模板和
[…]
1.3它们相应的整型或枚举型非类型模板参数具有相同的值

形式上,名称用于表示实体
basic。概念#5

每个表示实体的名称都由一个声明引入。

那么,是否Foo

然而,在最新的草案中有些变化。它引入了一个新的措辞“模板参数等效”<温度。类型#1

两个模板ID相同,如果

1.1它们的模板名称、运算符函数标识或文字运算符标识引用相同的模板,并且
1.2...
1.3它们相应的非类型模板参数在转换为模板参数类型后是模板参数等效的(见下文)


模板参数等价

两个值是模板参数等效的,如果它们是相同的类型和

它们是整型的它们的值是相同的

正如在其他答案中所说,Foo的推导类型

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

因此在转换为模板参数的类型后,这两个值是相同类型的,因此它们是模板参数等效的。所以,在c20标准下讨论这个例子,GCC仍然是错误的。

秦琦
2023-03-14

这都是描述decltype如何工作的规则。

[dcl.type.simple]

4对于表达式e,由decltype(e)表示的类型定义如下:

>

  • 如果e是命名结构化绑定的未加括号的id表达式([dcl.struct.bind]),则dectype(e)是结构化绑定声明规范中给出的引用类型;

    否则,如果e是未加密的id表达式或未加密的类成员访问,decltype(e)是由e命名的实体的类型。如果没有这样的实体,或者如果e命名了一组重载函数,则程序的格式不正确;

    否则,如果e是xvalue,decltype(e)T

    否则,如果e是左值,decltype(e)T

    否则,dectype(e)e的类型。

    当使用dectype(auto)时,e是用作对象(arg)初始化器的表达式。在OP中,这个表达式是x。它是一个未加括号的id表达式,因此dectype(x)将是由x命名的实体的类型。该类型是int const,因为conexpr说明符意味着const

    [dcl.constexpr]

    9对象声明中使用的constepr说明符将对象声明为const。此类对象应具有文字类型,并应初始化。在任何constexpr变量声明中,初始化的完整表达式应为常量表达式。

    因此,这里有一个可爱的代码示例修改,使GCC接受它。

    static_assert(std::is_same_v<Foo<42>, Foo<+x>>);
    

    为什么呢?这是因为x不再是一个id表达式。它是一个类型为int的普通旧prvalue表达式,具有与x相同的值。这就是decltype(auto)推导出的int

    总而言之,拒绝您的代码的编译器是正确的。我猜它会告诉你,对非类型模板参数使用dectype(自动)应该附带一个简短的免责声明。

  •  类似资料:
    • 我试图重载一个乘法运算符,但不想输入多个重载函数来考虑整数与浮点、整数与双精度、浮点与整数的乘法等等。。。我希望编写一个重载运算符来解释所有带浮点数、整数和double的乘法组合,并得到正确的返回类型。我得到的错误是,没有找到接受“Widget::Widget”类型的右操作数的运算符(或者没有可接受的转换)。我想这是因为我正在使用decltype来设置返回对象小部件的模板类型。如果返回不是模板对象

    • 在里面https://github.com/stlab/libraries/blob/main/stlab/concurrency/main_executor.hpp,我读到 decltype(f)的意义是什么,为什么不直接使用f?

    • 我在一个项目中,必须使用一个常量模板对象的引用作为另一个对象模板的参数。 简单地说,我想这样做: 问题是我不知道如何让它发生,我需要你的帮助。 在visual studio上,上述代码将产生以下错误:“C2971:具有非静态存储持续时间的变量不能用作非类型参数” 如果我尝试使用 ,则进行以下更改: 我得到以下错误“C2131:表达式未计算为常数” 好吧,我确实尝试了一些我在其他帖子上看到的关于类似

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

    • 我发现gcc和clang允许在非类型模板参数类型子句中使用。例如: [live demo gcc][live demo clang] 它是符合标准的功能还是一些gnu扩展?