3

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 } 
9
  • 1
    "this seems inconsistent with other entities, such as integers" is backwards. Integers are inconsistent with everything else. Commented Nov 4, 2020 at 22:05
  • I don't know what you are abbreviating as NTTP. Commented Nov 4, 2020 at 22:11
  • @BenVoigt non-type-template-parameter. But disregard that, it only seems to work for completely empty types. Commented Nov 4, 2020 at 22:12
  • 1
    Actually, clang gives a decently helpful diagnostic in this case: "f<cp>() doesn't work because it's not the address of a function with external linkage". gcc says nothing at all :( Commented Nov 4, 2020 at 23:00
  • 1
    @user4581301 At least in those days I never expected the compiler to help me much. Now that they're way better, I'm more disappointed in cases when they aren't helpful :p Commented Nov 4, 2020 at 23:20

1 Answer 1

3

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.

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

2 Comments

The limitation for potentially-constant expressions which are not constexpr seems a bit dated. literaltype or such would make more sense since that was introduced.
@Deduplicator Yeah, that's true. Literal type is used half a dozen times in expr.const. Maybe it should be considered a wording defect? I'm not sure about that though.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.