9

在回答这个问题时,我用 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();
}

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

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

编辑:对于“为什么”,请参阅DyP 的问题

4

1 回答 1

9

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

非类型、非模板模板参数的模板参数应为以下之一:

[...]

— 一个常量表达式 (5.19),它指定具有静态存储 > 持续时间和外部或内部链接的对象或具有外部或内部链接的函数的地址,包括函数模板和函数模板 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`
于 2013-04-07T20:37:07.920 回答