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
}
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 1
    "this seems inconsistent with other entities, such as integers" is backwards. Integers are inconsistent with everything else. – Ben Voigt Nov 04 '20 at 22:05
  • I don't know what you are abbreviating as NTTP. – Ben Voigt Nov 04 '20 at 22:11
  • @BenVoigt non-type-template-parameter. But disregard that, it only seems to work for completely empty types. – Vittorio Romeo Nov 04 '20 at 22:12
  • 1
    Actually, clang gives a decently helpful diagnostic in this case: "`f()` doesn't work because it's not the address of a function with external linkage". gcc says nothing at all :( – cigien Nov 04 '20 at 23:00
  • gcc wouldn't get nearly as much entertainment silently laughing at you if it flat out told you what the problem was, would it? Compiler's gotta have some fun. – user4581301 Nov 04 '20 at 23:12
  • @user4581301 Haha, yeah, I love that compiler, but it can be sadistic at times. Maybe I'm just a masochist ;) – cigien Nov 04 '20 at 23:15
  • Better than the good ol' days of *syntax error on line 31459* when the program is only 300 lines. Modern compilers *wish* they could be that annoying. – user4581301 Nov 04 '20 at 23:18
  • 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 – cigien Nov 04 '20 at 23:20
  • With you on that. Learned early to keep a few extra compilers around to throw at a program if I couldn't understand the error message. If Turbo C emitted nonsense maybe Watcom or cl could make more sense. – user4581301 Nov 04 '20 at 23:24

1 Answers1

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.

cigien
  • 57,834
  • 11
  • 73
  • 112
  • 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 Nov 04 '20 at 23:12
  • @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. – cigien Nov 04 '20 at 23:17