19

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

W.F.
  • 13,888
  • 2
  • 34
  • 81
  • 1
    `auto bar = [](auto i) -> std::enable_if_t { };` should be fine (and legal). – ildjarn Mar 27 '17 at 10:03
  • @ildjarn yes, but [std::integral_constant](http://en.cppreference.com/w/cpp/types/integral_constant) has overloaded constexpr conversion operator so I thought it should also work without `decltype(i)::value`, or shouldn't it? – W.F. Mar 27 '17 at 10:05
  • 1
    Actually the behavior affects SFINAE in case of function [as well](https://wandbox.org/permlink/1xKhzvXGSCec6tD3). I missed that fact before... – W.F. Mar 27 '17 at 10:14
  • 2
    This is not related to SFINAE. Here's a [minimal example](https://wandbox.org/permlink/tqyAogSOLYDrNNbg) that works on clang but not gcc. Language lawyer question? – Vittorio Romeo Mar 27 '17 at 10:45
  • @VittorioRomeo You're right... I'll try to edit the question to make it better. Thanks! – W.F. Mar 27 '17 at 10:47
  • 1
    Smells like a gcc bug to me... – Vittorio Romeo Mar 27 '17 at 11:08
  • I think that the real question is: [**"is `i` a valid `constant-expression`"**](http://eel.is/c++draft/expr.const#nt:constant-expression)? – Vittorio Romeo Mar 27 '17 at 11:24
  • `decltype(i){}` is then why `i` shouldn't? – W.F. Mar 27 '17 at 11:26
  • Both schemes are wrong, since a template parameter must be a constant expression, which in your case the function parameter is not. Naively, we could say that both compilers are right, since any of the templates in the examples is not instantiated. – 101010 Mar 27 '17 at 11:28
  • @101010 but it might be convertable to a constant expression, no? – W.F. Mar 27 '17 at 11:29
  • @W.F If you mean the function parameters I believe the answer is no. – 101010 Mar 27 '17 at 11:32
  • @101010 why do you think so? How does `decltype(i){}` differs from `i` itself in this context? – W.F. Mar 27 '17 at 11:37
  • @101010: it seems that the error occurs during parsing, though. `i` could be a *constant-expression* in some instantiations of `foo` - shouldn't gcc wait until `foo`'s instantiation to produce an error? – Vittorio Romeo Mar 27 '17 at 11:38
  • 1
    @W.F `decltype(i)` is a type. – 101010 Mar 27 '17 at 11:54
  • @101010 but `decltype(i){}` is an instance. gcc actually agrees that in case of `decltype(i){}` the code is legit for both examples. [ex1](https://wandbox.org/permlink/BoBhiRSUrEo2LZ3f), [ex2](https://wandbox.org/permlink/F0Zl832ddokqVa2a) – W.F. Mar 27 '17 at 11:58
  • 1
    Related: http://stackoverflow.com/questions/33872039/invalid-explicitly-specified-argument-in-clang-but-successful-compilation-in-gcc – Vittorio Romeo Mar 27 '17 at 12:59

2 Answers2

6

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.

Community
  • 1
  • 1
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 2
    You still have to demonstrate that the converted expression is a constant expression, but I think this is basically right. An example will also help, since this is a bit counter-intuitive. – T.C. Mar 27 '17 at 19:39
  • @T.C. I updated my answer - let me know if my proof looks solid or if I am missing something. – Vittorio Romeo Mar 28 '17 at 20:26
5

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.

101010
  • 41,839
  • 11
  • 94
  • 168