包含函数指针的类可以用作非类型模板参数吗?

cig*_*ien 6 c++ templates language-lawyer non-type c++20

考虑以下S包含函数指针的类,以及使用 lambda 初始化的该类的constexpr对象s

struct S 
{ 
    void (*f) (); 
};

constexpr S s { []{} };
Run Code Online (Sandbox Code Playgroud)

现在,如果我X使用 type 的非类型模板参数编写模板S,并s像这样实例化它:

template<S> struct X {};
using x = X<s>;
Run Code Online (Sandbox Code Playgroud)

clang 编译代码,但 gcc 抱怨:

error: '<lambda()>::_FUN' is not a valid template argument of type 'void (*)()' because '<lambda()>::_FUN' is not a variable
using x = X<s>;
             ^
Run Code Online (Sandbox Code Playgroud)

这是程序

代码对我来说似乎很好,我不确定错误消息指的是什么。那么这个代码有效吗?

请注意,如果X有一个引用类型为 to 的非类型模板参数,则两个编译器都接受代码S,如下所示:

template<S const &> struct X {};
Run Code Online (Sandbox Code Playgroud)

这个问题的灵感来自另一个类似的问题

cpp*_*ner 5

代码有效。

[temp.arg.nontype]/2 :

模板参数的用于非类型模板参数应为的类型的一个转换后的常量表达式([expr.const])模板参数

[表达式常量]/10 :

转换常量表达式类型T是一个表达式,隐式转换为类型T,其中,所述转换后的表达式是一个常量表达式和隐式转换序列仅包含[...]

(在这种情况下没有隐式转换。)

[expr.const]/11 :

常量表达式是 [...] 或其值满足以下约束的纯右值核心常量表达式:

  • 如果值是类类型的对象,则每个引用类型的非静态数据成员都引用一个实体,该实体是常量表达式的允许结果,
  • 如果值是指针类型,则它包含 [...] 非立即函数的地址 [...],
  • 如果值是指向成员函数的指针类型,[...],
  • 如果值是类或数组类型的对象,则每个子对象都满足该值的这些约束。

实体是常量表达式允许结果,如果 [...] 或者它是非直接函数。

(所有函数都是非直接的,除了consteval那些。[dcl.constexpr]

因此,s是类型 的有效转换常量表达式S。此外,它不受[temp.arg.nontype]/3(仅适用于对象的指针/引用)的约束。

也就是说,s是一个有效的模板参数。


我不确定错误消息指的是什么

这是胡说八道。

该错误是从invalid_tparm_referent_pGCC 内部发出的。它是从实现类非类型模板参数(4be5c72)时处理指向对象类型的指针的代码中提取的。显然,实现者忘记更新此函数以解决指向函数的情况。

我已将该错误报告为https://gcc.gnu.org/PR97700