12

I have the following piece of code which compiles on older gcc, but not on version 6 (works with -std=c++1z). Clang rejects it too, saying the object val doesn't have the right kind of linkage. I don't understand the difference. Isn't a constexpr variable of a pointer type supposed to work more or less transparently? Is there something I'm missing in the syntax that would allow this to work? Or is this violating some portion of the standard?

typedef void(*t_voidfn)();
template <t_voidfn> struct s {};
void fn() {
  static constexpr t_voidfn val = &fn;
  s<val> x;
}

On the other hand this one works.

typedef void(*t_voidfn)();
template <t_voidfn> struct s {};
void fn() {
  s<&fn> x;
}
7
  • 3
    The static keyword there doesn't make the variable private, it makes it a runtime value that will have persistent storage, to be initialized the first time the function is called... Commented Sep 11, 2016 at 20:16
  • 1
    @KerrekSB but I'm not taking the address of the variable, I'm using its value, which is initialized at compile time to be the address of something with apparently acceptable linkage, since the value alone works. Commented Sep 11, 2016 at 20:35
  • 2
    The error message is not very helpful. What it means to say is that the address of the function has to be expressed strictly as &fn or fn. This is simply a restriction specified in the Standard for C++14 and below, which was lifted for C++17. This is why the code compiles on both compilers in C++1z mode. cc @KerrekSB Commented Sep 11, 2016 at 20:40
  • @bogdan is this restriction explicitly required or was left unspecified before c++17. The reason I'm asking is that gcc-4.8.5 and gcc-5.3.0 accept the code as valid. Or is this a bug in gcc which was fix in gcc-6? Commented Sep 11, 2016 at 21:05
  • 1
    @bogdan I think you should write an answer so I can accept it. And I would appreciate a bit of extra information on what the standard says, in c++11, 14, 17, and how it changed. In any case thanks. Commented Sep 11, 2016 at 21:59

1 Answer 1

6

The first snippet is correct in C++17, but not in C++14 and 11.

For C++14, [temp.arg.nontype]/1 says:

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 a complete 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, where the id-expression is the name of an object or function, 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; or
  • a constant expression that evaluates to a null pointer value (4.10); or
  • a constant expression that evaluates to a null member pointer value (4.11); or
  • a pointer to member expressed as described in 5.3.1; or
  • a constant expression of type std::nullptr_t.

(I've included only the bullets that are directly relevant to pointers and pointers to members.)

Basically, the address of the function in your sample has to be expressed strictly as &fn or fn.

C++11 contains essentially the same wording minus a couple of clarifications introduced by defect reports between 11 and 14:

  • DR1570 clarified the bit about complete objects;
  • DR1398 amended by DR1666 added the last bullet.

For C++17, the restrictions have been relaxed as a result of the adoption of paper N4268 (rationale in N4198). The corresponding paragraph (2) now says:

A template-argument for a non-type template-parameter shall be a converted constant expression (5.20) of the type of the template-parameter. For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

  • a subobject (1.8),
  • a temporary object (12.2),
  • a string literal (2.13.5),
  • the result of a typeid expression (5.2.8), or
  • a predefined __func__ variable (8.4.1).

[ Note: If the template-argument represents a set of overloaded functions (or a pointer or member pointer to such), the matching function is selected from the set (13.4). — end note ]

N4198 contains good explanations for each of those bullets.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.