mercredi 24 juin 2015

decltype() of captured variable in lambda: GCC bug and/or Clang bug?

I've checked the GCC buglist and the Clang buglist and don't see anything relevant yet.

This Wandbox link shows some C++11/C++14 code exercising decltype(x) and decltype((x)) for various kinds of x captured by lambdas. GCC and Clang give different answers for this code. Which of them, if either, is correct?

Here's the offending snippet:

// inside main()
int i = 42;
int &j = i;
[j=j](){
    static_assert(std::is_same<decltype(j), GCC(const) int>::value,""); // A
    static_assert(std::is_same<decltype((j)), const int&>::value,""); // B
}();
[=](){
    static_assert(std::is_same<decltype(j), int&>::value,""); // C
    static_assert(std::is_same<decltype((j)), CLANG(const) int&>::value,""); // D
}();

I believe that in both cases, the thing that is actually captured(*) is a (non-const) int initialized to a copy of j's value (that is to say, i's value). Since the lambda isn't marked mutable, its operator() is going to be a const member function. With those prerequisites out of the way, let's proceed...

On line // A, GCC tells me that the decltype of the explicitly init-captured j is const int, when I'm almost positive that it ought to be int (per Clang).

On line // B, both compilers agree that (j) is an lvalue referring to a const int (since the lambda is not marked mutable); this makes perfect sense to me.

On line // C, both compilers agree that j is a name referring to the int& declared on line 2. This is a consequence of 5.1.2 [expr.prim.lambda]/19, or rather, a consequence of the-thing-that-happens-when-that-clause-is-not-being-invoked. Inside a [=] lambda, the name j refers to the j in the outer scope, but the expression (j) refers to the (j) that would exist if j were to have been captured. I don't fully understand how this works or why it's desirable, but there it is. I'm willing to stipulate that this is not a bug in either compiler.

On line // D, Clang tells me that (j) is an lvalue referring to a const int, whereas GCC tells me that it's an lvalue referring to a non-const int. I'm pretty sure that Clang is right and GCC is wrong; decltype((j)) should be the same whether j is captured implicitly or explicitly.

So:

  • Are my explanations correct (according to the Standard)?
  • Does the correct answer change between C++11 and C++14 (and C++1z)?
  • Are // A and // D both bugs in GCC?
  • Have these bugs been filed already?

(*) — In fact nothing is technically captured by the second lambda, because it doesn't use j in any evaluated context. That's why lines // A and // C give different answers. But I don't know any nice terminology for the-thing-that-is-being-done-to-j, so I'm just saying "captured".

Aucun commentaire:

Enregistrer un commentaire