Шаблонный класс может захватить свой собственный указатель this
в lambda:
template <typename T>
class Foo {
public:
void foo(void) {}
auto getCallableFoo(void) {
return [this]() { this->foo(); };
}
};
Этот и все другие примеры Foo
могут быть протестированы с использованием следующего кода:
int main()
{
Foo<int> f;
auto callable = f.getCallableFoo();
callable();
}
Однако, если вместо этого используется init-capture, это больше не работает с GCC:
auto getCallableFoo(void) {
return [ptr = this]() { ptr->foo(); };
}
Сообщение об ошибке (из GCC 5.1):
error: ‘Foo<T>::getCallableFoo()::<lambda()>::__ptr’ has incomplete type
Clang 3.7, похоже, компилирует и запускает этот код без ошибок. (Я фактически использую версию, скомпилированную из исходного кода до версии 3.7, но я не ожидаю, что это сломается с тех пор.)
Предполагаемый захват должен вести себя как присвоение auto
, но следующий код работает без ошибок в GCC:
// New method in Foo:
auto getPtr(void) {
return this;
}
// Usage:
auto ptr = f.getPtr();
ptr->foo();
Так почему же значение ptr
не может захватить this
в GCC? Это ошибка?
Еще одно соображение состоит в том, что в соответствии с CppReference, this
рассматривается как отдельный синтаксический случай из любого другого типа списка захвата. Таким образом, это может быть одним из намеков на то, почему GCC рассматривает эти случаи по-разному. Но мне непонятно, какая (если таковая имеется) специальная обработка для этого особого случая или почему это вообще особый случай.
EDIT: Похоже, что это работает:
return [ptr = static_cast<decltype(this)>(this)]() { ptr->foo(); };
Это не имеет для меня никакого смысла, потому что decltype
(в отличие от auto
) точно указывает тип своего аргумента, поэтому static_cast
не должен влиять ни на что.
EDITS 2,3,4: Здесь приведен полный список выражений, которые я пробовал с обоими компиляторами, с комментариями, указывающими, какой компилятор принимает каждое выражение:
[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
Для [ptr{this}]
моя версия Clang (предварительная версия 3.7) предупреждает, что интерпретация изменится; в настоящее время он отображает список инициализаторов, но предположительно более поздние версии (или уже делают) выводят тип this
в соответствии с новыми правилами auto
из N3922.
Меня шокирует, что GCC разрешает [ptr{this}]
, но не [ptr(this)]
. У меня нет объяснений.