9

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.

Community
  • 1
  • 1
Synxis
  • 9,236
  • 2
  • 42
  • 64
  • I'm wondering whether or not `&function` is actually a valid initializer for `constexpr`. Both compilers seem to think so. But it seems iffy to me. That isn't known at compile time, only at link time. – Omnifarious Apr 07 '13 at 20:34
  • `&function` is the address of a function, so for me it is known at compile time... For example, you can pass a pointer to function as a template argument. – Synxis Apr 07 '13 at 20:37
  • 1
    A pointer to a function is one thing... if this was part of a shared library, for example, how could the address possibly qualify as a `constexpr`? – Brett Hale Apr 07 '13 at 20:46
  • @BrettHale I don't know exactly, but I suspect the pointer to function to be an offset to the beginning of the dll code, and it is relocated when the dll is loaded. Anyway, I know that it works for libraries, since I already used it in template parameters (it was in C++03, though). – Synxis Apr 07 '13 at 21:01

1 Answers1

9

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`
dyp
  • 38,334
  • 13
  • 112
  • 177
  • 3
    This seems like an oversight. If you assign a `constexpr` thing to a `constexpr` and then assign that to another `constexpr`, it should be transitive. – Omnifarious Apr 07 '13 at 20:57
  • 3
    @Omnifarious It **is** a `constexpr`. But it's explicitly forbidden as a template non-type argument. – dyp Apr 07 '13 at 21:04
  • Well, that's interesting. Explicitly not treated orthogonally. I wonder what the reasoning is. Though, I have a strong suspicion. My guess is that objects that can be relocated by the linker are referred to by name in the mangling rules for templates. Requiring compilers to carry around the original name for `constexpr` things like that is rather onerous and possibly impossible in some scenarios. – Omnifarious Apr 08 '13 at 14:05
  • @DyP In the sentence you put in bold, I don't see where it explicitely say `constexpr func f = &function;` to be illegal (maybe I missed a part of your reasoning). Also I think the function can always be available at compile-time, because you can use offsets to start of machine code as function address for example (and also I made it working on several compilers, though not using `constexpr` nor `static`: `template struct A {}; void func() {} A<&func> object;`). – Synxis Apr 08 '13 at 16:22
  • @Synxis I enhanced my answer, but couldn't answer the "why", therefore asked it as [a separate question](http://stackoverflow.com/q/15885399/420683) – dyp Apr 08 '13 at 17:44
  • Ok, I understand the point. I will see the "why" in your question. – Synxis Apr 08 '13 at 18:24