This seems like a gcc bug. I reported it as issue #80242.
gcc complains about the validity of i as a template argument:
error: template argument 1 is invalid
I've followed the C++ grammar from trailing-return-type to template-argument, which needs to be a constant-expression:
template-argument:
- constant-expression <-
- type-id
- id-expression
The real question then becomes: "is i a valid constant-expression?".
I think the answer is "yes", because §8.20.4 [expr.const] says:
A converted constant expression of type T is an expression, implicitly converted to type T, where the converted expression is a constant expression and the implicit conversion sequence contains only:
- user-defined conversions,
[...]
(Note: Such expressions may be used in new expressions, as case expressions, as enumerator initializers if the underlying type is fixed, as array bounds, and as non-type template arguments. )
There is a sequence of implicit conversions that, starting from i, will produce a converted constant expression which is a constant expression. Given:
template <int>
struct bar { };
template <class I>
auto foo(I i) -> bar<i> { }
int main()
{
foo(std::integral_constant<int, 1>{}); // (0)
}
In the context of the function call at (0), the argument i is an instance of std::integral_constant<int, 1>.
std::integral_constant provides a constexpr user-defined conversion to the underlying value_type.
Converted constant expressions explicitly allow user-defined conversions, as seen above in §8.20.4 [expr.const].
std::integral_constant::operator value_type() will return the non-type template argument 1. This is a core constant expression as it doesn't violate any of the rules specified in §8.20.2 [expr.const].
Therefore, the converted constant expression is a constant expression.