Reputation: 29952
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
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
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