静态constexpr指针指向函数,编译器之间的区别

static constexpr pointer-to-function, difference between compilers

本文关键字:编译器 之间 区别 函数 constexpr 指针 静态      更新时间:2023-10-16

在回答这个问题时,我用gcc(已编译的代码)和clang(已拒绝的代码)尝试了以下代码:

typedef long (*func)(int);
long function(int) { return 42; }
struct Test
{
    static constexpr func f = &function;
};
template<func c>
struct Call
{
    static void f()
    {
        c(0);
    }
};
int main()
{
    Call<Test::f>::f();
}

我不确定哪个编译器是正确的,虽然我认为Test::f的constexpr初始化是可以的。clang输出的错误是:

error: non-type template argument for template parameter of pointer type 'func'
       (aka 'long (*)(int)') must have its address taken
    哪个编译器是正确的?
  1. 如果clang是正确的,为什么,这个错误到底意味着什么?

编辑:"为什么",看到多元印刷的问题。

14.3.2模板非类型参数[temp.arg.nontype]

非类型、非模板模板形参的模板实参必须是:

[…]

——一个常量表达式(5.19),用于指定具有静态存储> duration和外部或内部链接的对象的地址,或具有外部或内部链接的函数的地址,包括函数模板和函数模板id,但不包括非静态类成员,表示为(忽略括号)&id-expression,除了&引用函数或数组的可以省略,引用模板形参的可以省略;[…]

(n3485,重点是我的)

我不知道为什么它被限制,但我认为这可能与函数地址在编译时不可用的事实有关(可能有模板实例化目的的替代品)。


编辑:由于Synxis的后续问题(评论),增强了答案

constexpr func = &function;

^这是格式良好的;您可以使用函数的地址来初始化constexpr对象。问题是,它明确禁止使用指针作为非类型模板参数,除了形式&identifier:

using My_Call     = Call < &function >;  // fine
constexpr func mypointer = &function;    // fine
using My_Ind_Call = Call < func >;       // forbidden, argument not of form `&id`