static constexpr pointer-to-function, difference b

2019-02-16 16:33发布

问题:

When answering this question, I tried the following code with gcc (code compiled) and 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();
}

I am not sure which compiler is right, although I think the constexpr initialization of Test::f is ok. The error clang outputs is:

error: non-type template argument for template parameter of pointer type 'func'
       (aka 'long (*)(int)') must have its address taken
  1. Which compiler is right ?
  2. If clang is right, why , and what does this error really means ?

EDIT: for the "why", see DyP's question.

回答1:

14.3.2 Template non-type arguments [temp.arg.nontype]

A template-argument for a non-type, non-template template-parameter shall be one of:

[...]

— a constant expression (5.19) that designates the address of an object with static storage > duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; [...]

(n3485, emphasis mine)

I don't know exactly why it's been limited, but I think it might be related to the fact that the function address is not available at compile time (there might be a substitute for template instantiation purposes).


Edit: Enhanced answer due to a follow-up question (comment) of Synxis

constexpr func = &function;

^ this is well-formed; you can use the address of a function to initialize a constexpr object. The problem is that it's explicitly forbidden to use pointers as non-type template arguments other than of the form &identifier:

using My_Call     = Call < &function >;  // fine

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