Consider the following code snippet:
template <bool> struct B { }; template <typename T> constexpr bool pred(T t) { return true; } template <typename T> auto f(T t) -> decltype(B<pred(t)>{}) { }
clang++ (trunk) compiles the code
g++ (trunk) fails compilation with the following error:
src:7:34: error: template argument 1 is invalid auto f(T t) -> decltype(B<pred(t)>{}) ^ src:7:34: error: template argument 1 is invalid src:7:34: error: template argument 1 is invalid src:7:34: error: template argument 1 is invalid src:7:34: error: template argument 1 is invalid src:7:34: error: template argument 1 is invalid src:7:25: error: invalid template-id auto f(T t) -> decltype(B<pred(t)>{}) ^ src:7:36: error: class template argument deduction failed: auto f(T t) -> decltype(B<pred(t)>{}) ^ src:7:36: error: no matching function for call to 'B()' src:1:24: note: candidate: 'template<bool <anonymous> > B()-> B<<anonymous> >' template <bool> struct B { }; ^ src:1:24: note: template argument deduction/substitution failed: src:7:36: note: couldn't deduce template parameter '<anonymous>' auto f(T t) -> decltype(B<pred(t)>{}) ^
live example on godbolt.org
Even though g++'s diagnostic is misleading, I assume that the problem here is that t
is not a constant expression. Changing the code to...
decltype(B<pred(T{})>{})
...fixes the compilation error on g++: live example on godbolt.org
What compiler is behaving correctly here?
GCC is wrong. There is no rule that prevents using a function's parameters in a constant expression in this way.
However, you cannot use the value of the parameter in such a context, and the set of types T
for which f
is callable is quite restricted. To see why, we need to consider what constructs will be evaluated when evaluating the expression pred(t)
:
// parameters renamed for clarity template <typename U> constexpr bool pred(U u) { return true; } template <typename T> auto f(T t) -> decltype(B<pred(t)>{});
The evaluation semantics for the call pred(t)
are as follows:
pred
's parameter u
from f
's parameter t
pred
, which trivially creates a bool
value true
u
So, f
is only callable for types T
for which the above only involves constructs that are valid during constant evaluation (see [expr.const]p2 for the rules). The requirements are:
T
must be a literal typeu
from t
must be a constant expression, and in particular, must not perform an lvalue-to-rvalue conversion on any member of t
(because their values are not known), and must not name any reference member of t
In practice, this means that f
is callable if T
is an empty class type with a defaulted copy constructor, or if T
is a class type whose copy constructor is constexpr
and does not read any members of its argument, or (strangely) if T
is std::nullptr_t
(although clang currently gets the nullptr_t
case wrong).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With