Can a class containing a function pointer be used as a non-type template parameter?
Asked Answered
H

1

7

Consider the following class S containing a function pointer, and a constexpr object s of that class initialized with a lambda:

struct S 
{ 
    void (*f) (); 
};

constexpr S s { []{} };

Now if I write a template X with a non-type template parameter of type S, and instantiate it over s like this:

template<S> struct X {};
using x = X<s>;

clang compiles the code, but gcc complains with:

error: '<lambda()>::_FUN' is not a valid template argument of type 'void (*)()' because '<lambda()>::_FUN' is not a variable
using x = X<s>;
             ^

Here's the program.

The code seems fine to me, and I'm not sure what the error message is referring to. So is this code valid?

Note that both compilers accept the code if X instead has a non-type template parameter of reference type to S, like this:

template<S const &> struct X {};

This question was inspired by another similar question.

Hagio answered 3/11, 2020 at 2:24 Comment(3)
Seems likely to be a variant of this GCC bug.Kenning
@DavisHerring I think this is a different bug. This error message explicitly mentions "is not a variable".Intergrade
@DavisHerring It does look related, and there are a number of posts about linkage issues with gcc, e.g. this. The diagnostic seems different though, but if it is related, maybe that bug report should be updated.Hagio
I
6

The code is valid.

[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.

[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 [...]

(There's no implicit conversion in this case.)

[expr.const]/11:

A constant expression is [...] or a prvalue core constant expression whose value satisfies the following constraints:

  • if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a constant expression,
  • if the value is of pointer type, it contains [...] the address of a non-immediate function [...],
  • if the value is of pointer-to-member-function type, [...],
  • if the value is an object of class or array type, each subobject satisfies these constraints for the value.

An entity is a permitted result of a constant expression if [...] or if it is a non-immediate function.

(All functions are non-immediate except for the consteval ones. [dcl.constexpr])

Therefore, s is a valid converted constant expression of type S. In addition, it is not subject to [temp.arg.nontype]/3 (which only applies to pointer/reference to objects).

That is, s is a valid template argument.


I'm not sure what the error message is referring to

It's nonsense.

The error is emitted from invalid_tparm_referent_p inside GCC. It was extracted from code for handling pointer to object type when class non-type template paramter was implemented (4be5c72). Apparently, the implementer forgot to update this function to account for the pointer-to-function case.

I've reported the bug as https://gcc.gnu.org/PR97700.

Intergrade answered 3/11, 2020 at 13:52 Comment(1)
Yes, this looks right. Thanks for filing the bug, I'll accept the answer as soon as the bug is confirmed.Hagio

© 2022 - 2024 — McMap. All rights reserved.