geza
geza

Reputation: 29952

Can the address of a template parameter be used in instantiating another template?

I read a trick somewhere how to convert a constant literal value to a static variable (this can be useful in a template). Check out the makeStatic function in my example code which does this.

I tried to use the result of makeStatic to another call to makeStatic:

template <auto VALUE>
consteval const auto &makeStatic() {
    return VALUE;
}

struct Foo {
};

struct Bar {
    const Foo *x;
};

Foo foo;

int main() {
    constexpr Bar bar{&makeStatic<Foo{}>()};
    // constexpr Bar bar{&foo};

    makeStatic<bar>();
}

This example is compiled by clang and MSVC, but gcc rejects it (godbolt):

<source>: In function 'int main()':
<source>:19:20: error: no matching function for call to 'makeStatic<bar>()'
   19 |     makeStatic<bar>();
      |     ~~~~~~~~~~~~~~~^~
<source>:2:23: note: candidate: 'template<auto VALUE> consteval const auto& makeStatic()'
    2 | consteval const auto &makeStatic() {
      |                       ^~~~~~~~~~
<source>:2:23: note:   template argument deduction/substitution failed:
<source>:19:20: error: the address of 'Foo()' is not a valid template argument
   19 |     makeStatic<bar>();
      |     ~~~~~~~~~~~~~~~^~

Which compiler is correct?

Note: if you use the other bar definition, which uses a global variable to initialize bar, then gcc compiles the code.

Update: I found a similar bug report in gcc's bugzilla, so it seems this is a gcc bug. I'm still wondering where the standard guarantees that my code is well-formed.

Upvotes: 3

Views: 140

Answers (2)

Artyer
Artyer

Reputation: 40811

This is the complete list of things not allowed in a pointer/reference object/subobject of a template parameter ([temp.arg.nontype]p3):

For a non-type template-parameter of reference or pointer type, or for each non-static data member or reference or pointer type in non-type template-parameter of class type or subobject thereof, the reference or pointer value shall not refer to or be the address of (respectively):

  • a temporary object ([class.temporary])
  • a string literal object ([lex.string])
  • the result of a typeid expression ([expr.typeid])
  • a predefined __func__ variable ([dcl.fct.def.general]), or
  • a subobject ([intro.object]) of one of the above.

A template parameter object is not a temporary, since it was never a prvalue ([class.temporary]p1):

Temporary objects are created

  • when a prvalue is converted to an xvalue ([conv.rval]),
  • when needed by the implementation to pass or return an object of trivially copyable type (see below), and
  • when throwing an exception ([except.throw]).

And it definitely isn't a string literal object / result of a typeid expression / __func__ / a subobject, so it should be accepted.

Upvotes: 3

Davis Herring
Davis Herring

Reputation: 39818

This is a GCC bug; the address of a template parameter object (which exists only for template parameters of class type!) is certainly a constant expression which can be used as another template argument. Template parameter objects are explicitly mentioned as being usable in constant expressions ([expr.const]/4.5).

Upvotes: 3

Related Questions