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