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

在回答this question时,我使用gcc(code compiled)和clang(code rejected)尝试了以下代码:

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
  • 哪个编译器是对的?

  • 如果铿锵是对的,为什么,这个错误究竟意味着什么?

EDIT :对于"why",请参阅DyP's question .

回答(1)

2 years ago

14.3.2模板非类型参数[temp.arg.nontype]非类型非模板模板参数的模板参数应为以下之一:[...] - 一个常量表达式(5.19)指定具有静态存储>持续时间和外部或内部链接的对象的地址或具有外部或内部链接的函数,包括函数模板和函数模板-id,但不包括非静态类成员,表示(忽略括号)为&id-表达式,除非如果名称引用函数或数组,可以省略&,如果相应的模板参数是引用,则省略; [...]

(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`