A templated class can capture its own this
pointer in a lambda:
template <typename T>
class Foo {
public:
void foo(void) {}
auto getCallableFoo(void) {
return [this]() { this->foo(); };
}
};
This and all other Foo
examples can be tested using the following code:
int main()
{
Foo<int> f;
auto callable = f.getCallableFoo();
callable();
}
However, if instead an init-capture is used, this no longer works with GCC:
auto getCallableFoo(void) {
return [ptr = this]() { ptr->foo(); };
}
Error message (from GCC 5.1):
error: ‘Foo<T>::getCallableFoo()::<lambda()>::__ptr’ has incomplete type
Clang 3.7 appears to compile and run this code without error. (I'm actually using a version compiled from source from before 3.7 was released, but I don't expect this has broken since then.)
Init-capture is supposed to behave like assignment to auto
, but the following code appears to work without error in GCC:
// New method in Foo:
auto getPtr(void) {
return this;
}
// Usage:
auto ptr = f.getPtr();
ptr->foo();
So why isn't the ptr
value able to capture this
in GCC? Is this a bug?
One other consideration is that, according to CppReference, this
is treated as a separate syntactical case from every other capture-list type. So that may be one hint toward why GCC treats these cases differently. But it is not clear to me what (if any) special handling is done for this special case, or why it's a special case at all.
EDIT: It appears that this does work:
return [ptr = static_cast<decltype(this)>(this)]() { ptr->foo(); };
This makes no sense to me, because decltype
(unlike auto
) infers exactly the type of its argument, so the static_cast
shouldn't actually be affecting anything.
EDITS 2,3,4: Here's a complete list of expressions that I've tried with both compilers, with comments indicating which compiler(s) accept each expression:
[this]() { this->foo(); }; // Both: work
[ptr = this]() { ptr->foo(); }; // GCC fails
[ptr = static_cast<decltype(this)>(this)]() { ptr->foo(); }; // Both: works (!!!)
[ptr(this)]() { ptr->foo(); }; // GCC fails
[ptr{this}]() { ptr->foo(); }; // GCC works (!!!!!!!!), Clang doesn't work (infers initializer list)
[ptr = {this}]() { ptr->foo(); }; // Both: fail (infers initializer list)
[ptr = &*this]() { ptr->foo(); }; // Both: work
[ptr = &*(this)]() { ptr->foo(); }; // Both: work
For [ptr{this}]
, my version of Clang (a pre-release 3.7) warns that the interpretation will change; currently it infers an initializer list, but presumably later versions will (or already do) infer the type of this
in accordance with the new auto
rules from N3922.
It shocks me that GCC permits [ptr{this}]
but not [ptr(this)]
. I have no explanation for this.
This is a bug. I've submitted a GCC bug report for this problem. It has now been fixed in GCC's trunk.
As noted by Revolver_Ocelot, &*
appears to force g++
to perform the correct type-deduction. My current workaround (which is inside a macro taking some pointer expression that might be this
) is therefore to capture [ptr = &*(ptr_expr)]
.
As noted above, GCC's Jason Merrill has fixed this in GCC's trunk. He comments that the this
pointer requires special handling in lambda captures; specifically, it is treated as though it were not a dependent type. Previously, this special handling applied to [this]
but not to [ptr = this]
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With