Why are `const` pointers to functions not usable in a constant expression?

Viewed 189

Consider the following template:

using IntFnPtr = int(*)(int);
template <IntFnPtr> void f() { }

And these tests:

int g(int) { }

int main()
{
    f<&g>(); // OK

    const IntFnPtr cp = &g;
    f<cp>(); // Error -- why? 

    constexpr IntFnPtr cexprp = &g;
    f<cexprp>(); // OK
}

Why is the attempt to instantiate f with cp ill-formed? The compiler complains about:

> error: no matching function for call to 'f'

live example on godbolt.org


Note that this seems inconsistent with other entities, such as integers:

template <int> void f() { }

int main()
{
    f<5>(); // OK

    const int ci = 5;
    f<ci>(); // OK

    constexpr int cexpri = 5;
    f<cexpri>(); // OK
}
1 Answers

To start, there is temp.arg.nontype#2:

A template-argument for a non-type template-parameter shall be a converted constant expression ([expr.const]) of the type of the template-parameter.

[Note 1: If the template-argument is an overload set (or the address of such, including forming a pointer-to-member), the matching function is selected from the set ([over.over]). — end note]

We can then follow that to expr.const#10:

A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only ...

From that rule, we can see that the variable cp can't possibly be a constant expression since it is not even potentially-constant according to expr.const#3:

A variable is potentially-constant if it is constexpr or it has reference or const-qualified integral or enumeration type.

So cp is not a valid template-argument for a non-type template parameter, and you get an error.

Note that this is where the apparent inconsistency arises with ci. Since ci has a const-qualified integral type, it can be used as a template-argument for a non-type template parameter.

Similarly, all the other calls to f are allowed as well, since the template-argument in each case is a constant expression:

  1. g is a function with external linkage, and so its address is a constant expression.

  2. 5 is a constant integral expression.

  3. cexprp and cexpri are both constexpr variables.

Related