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
}();
where:
#ifdef __clang__
#define CLANG(x) x
#define GCC(x)
#else
#define GCC(x) x
#define CLANG(x)
#endif
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".
I believe that both compilers are wrong for (A) and gcc is wrong for (D).I believe that gcc is wrong for (A) and (D), while clang is correct for both.
The relevant sections of [expr.lambda.prim] are:
and
decltype(j)
is not an odr-use ofj
, therefore no such transformation should be considered. Thus, in the case of[=]{...}
,decltype(j)
should yieldint&
. However, in the case of an init-capture, the behavior is as if there was a variable of the formauto j = j;
, and the variablej
refers to the same unnamed non-static data member with no such transformation necessary. So in the case of[j=j]{...}
,decltype(j)
should yield the type of that variable - which isint
. It is definitely notconst int
. That is a bug.The next relevant section:
The example further illustrates that
decltype(j)
should beint&
in the implicit copy case and also demonstrates thatdecltype((j))
is treated as ifx
were the corresponding data member that would have been declared: which isint const&
in both cases (as the lambda is notmutable
andj
is an lvalue). Your (C) and (D) cases exactly mirror ther1
,r2
declarations in the example. Which, while the examples are not normative, certainly suggests that gcc is in the wrong for having different behavior.