Consider the following code:
struct wrapper {
int& ref;
constexpr wrapper(int& ref) : ref(ref) {}
};
template <auto&& X>
void fun1() {}
template <wrapper X>
void fun2() {
fun1<X>();
}
int main() {
static int val = 22;
fun2<val>();
}
fun2<X>()
takes in a class as non-type template parameter while fun1<X>()
takes in a forwarding reference to any object passed to it.
The above fails to compile in GCC with the error shown below but compiles just fine in Clang.
<source>:11:12: error: the address of 'wrapper{val}' is not a valid template argument
11 | fun1<X>();
| ~~~~~~~^~
Which compiler is correct here? Is this supposed to be a bug in either of the two compilers?
I tried to change template <wrapper X>
to template <const wrapper& X>
, but to no avail, as it fails on both GCC and Clang.
Your code is valid. This is possibly a GCC bug. I've submitted a bug report, see GCC Bug 113242 - g++ rejects-valid template argument of class type containing an lvalue reference.
For a non-type template-parameter of reference or pointer type, or for each non-static data member of reference or pointer type in a 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.
The reference auto&& X
refers to an object wrapper X
that is none of those, so it should be a valid template argument.
Note that what happens in main
is effectively:
int main() {
static int val = 22; // OK, obviously
constexpr int& ref = val; // OK, can bind reference to static storage duration object
constexpr wrapper w = ref; // OK, can create object containing such reference
fun2<w>(); // OK, but GCC errors in fun2's definition
}
Also note that the unqualified-id X
in fun1<X>()
is an lvalue of type const wrapper
([expr.prim.id.unqual] p2).
Therefore, the reference binding is direct and no temporaries are involved.
Both compilers are correct in rejecting
template <const wrapper& X> void fun2() { /* ... */ } int main() { static int val = 22; fun2<val>(); }
... because fun2<val>()
makes X
bind to a temporary wrapper
object, created within the function call to fun2
.