Reputation: 13988
Consider the minimal example:
template <int>
struct bar { };
int main()
{
[](auto i) -> bar<i> { return {}; };
}
Or even:
template <int>
struct bar {};
template <class I>
auto foo(I i) -> bar<i> {}
clang compiles both forms without problems but gcc find the usages invalid (ex. 1), (ex. 2)
The question might look silly however the type of parameter can have the constexpr conversion operator overloaded (in this case type of i
deduced from value passed to lambda/foo to int
in constexpr manner) and in this scenario it would be quite convenient not to be forced to do some workarounds to access it directly...
Upvotes: 19
Views: 851
Reputation: 93364
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 typeT
, 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.
Upvotes: 6
Reputation: 42929
I believe that both your examples are wrong. Based on the standard wording 5.20 Constant expressions [expr.const]/p2.7:
2 A conditional-expression e is a core constant expression unless the evaluation of e, following the rules of the abstract machine (1.9), would evaluate one of the following expressions:j
...
- an lvalue-to-rvalue conversion (4.1) unless it is applied to:
Function parameters can't be constant expressions since in order to pass them as template parameters you're applying lvalue to rvalue conversion on them.
In both CLANG and GCC if you instatiate the templates you are going to get an error based on the wording above. I believe that since in the examples non of the template is instatiated no diagnostic is required thus both compilers are correct.
Upvotes: 5