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.