【发布时间】:2016-04-25 15:58:09
【问题描述】:
模板类可以在 lambda 中捕获自己的 this 指针:
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 值不能在 GCC 中捕获 this 呢?这是一个错误吗?
另一个考虑因素是,according to CppReference、this 被视为与所有其他捕获列表类型分开的 句法 案例。因此,这可能是 GCC 以不同方式对待这些案例的一个暗示。但我不清楚对这种特殊情况做了什么(如果有的话)特殊处理,或者为什么它是一个特殊情况。
编辑:看来这确实工作:
return [ptr = static_cast<decltype(this)>(this)]() { ptr->foo(); };
这对我来说毫无意义,因为decltype(与auto 不同)推断准确其参数的类型,因此static_cast 实际上不应该影响任何东西。 p>
EDITS 2,3,4:这是我在两种编译器中都尝试过的表达式的完整列表,cmets 指示哪些编译器接受每个表达式:
[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)警告解释会改变;目前它推断出一个初始化列表,但可能以后的版本将(或已经这样做)推断thisin accordance with the new auto rules from N3922的类型。
令我震惊的是 GCC 允许 [ptr{this}] 但不允许 [ptr(this)]。我对此没有任何解释。
【问题讨论】:
-
有趣。请注意,在 GCC 4.9.3 下将其更改为
[ptr = static_cast<const Foo<T>* const>(this)]对我有效(一旦将foo和getCallableFoo声明为const,它们应该是这样)。 -
@Yuushi 嗯。我希望它可以在宏
BIND_MEMBER_TO_THIS中工作,它只是将任意成员函数绑定到对象的this指针。我想使用更通用的BIND_MEMBER_TO_OBJ_PTR宏来实现宏,它会初始化捕获指针,但现在我正在解决(明显的)错误,只需使用[this]而不是重写整个 lambda利用另一个宏。至于foo和getCallableFoo,我在这里尽可能地通用——foo()可以是const或非const。 -
请注意,宏不能包含强制转换,除非使用
decltype。啊啊啊……看来static_cast<decltype(this)>(this),虽然看起来很傻,但确实有效。我将把它添加到问题中。 -
如果您使用简单的大括号初始化器而不是
=,是否会出现相同的错误? 即[ptr(this)]() { ... } -
有趣的是,
[ptr = &*this]工作...
标签: c++ templates inheritance lambda c++14