Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can one use parameter value directly (not its type but value itself) in trailing return type syntax

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...

like image 463
W.F. Avatar asked Mar 27 '17 10:03

W.F.


1 Answers

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.

like image 169
Vittorio Romeo Avatar answered Oct 22 '22 02:10

Vittorio Romeo