23
votes

When answering this SO question, I found in the Standard (already C++03, still in C++11) that you can only use addresses as non-type template arguments if they're of the form & id-expression (plus some exceptions).

But I couldn't answer why this is the case.

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)

Example:

using TFoobar = int (*)();
template < TFoobar tp > struct foo_struct{};

int foobar() { return 42; }
constexpr TFoobar pFoobar = &foobar;

foo_struct < &foobar > o0; // fine
foo_struct < pFoobar > o1; // ill-formed

I guess it has something to do with the translation phases, namely the compiler doesn't know much about addresses. Yet, why isn't it allowed? Shouldn't it be possible for the compiler to use something similar to macro substitution to replace pFoobar with &foobar?

2
I'd guess it is a matter of making conservative changes. C++11 is full of really strict ways for the language to do things in order to make things easier for the compiler implementers. It might be possible to extend C++11 to allow for the above, but that is a matter for C++14 or beyond! - Yakk - Adam Nevraumont
There's a proposal to remove these restrictions. - dyp
Lifting this restriction will greatly improve the implementation of "delegates". Right now, if a we use a member fn as template param, there seems to be no way to "generate" or even "forward" this param via helper class! The user must type the expression one way or another. - user362515

2 Answers

2
votes

Consider classes Foo<&X> and Foo<&Y>, both with a static member int Bar. The linker must be able to tell whether your program has 1 or 2 Bar objects. Now consider that the linker is also the party most likely responsible for assigning values to &X and &Y.

Look at the Standard again. As it's written, the compiler doesn't need to communicate the actual address to the linker. Instead, it passes the id-expression. Linkers are already quite capable of determining whether two id-expression's are the same, even before assigning a numerical address to them.

0
votes
  1. It cannot be a variable, since variables are only set at runtime.
  2. It cannot be a constexpr, since the value of an address cannot be known at compile time; in most cases it will only be fixed after relocation prior to execution.
  3. It might theoretically be an arithmetic expression (even though it isn't allowed in the standard), but in the common case of an address of an array element, you can simply use &arr[i] instead of arr + i.