GCC 无法使用 init-capture 捕获指向模板类型的“this”指针
Posted
技术标签:
【中文标题】GCC 无法使用 init-capture 捕获指向模板类型的“this”指针【英文标题】:GCC can't capture 'this' pointer to templated type using init-capture 【发布时间】: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
[ptrthis]() 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
对于[ptrthis]
,我的 Clang 版本(预发布 3.7)警告解释会改变;目前它推断出一个初始化列表,但可能以后的版本将(或已经这样做)推断this
in accordance with the new auto
rules from N3922的类型。
令我震惊的是 GCC 允许 [ptrthis]
但不允许 [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]
工作...
【参考方案1】:
这是一个错误。
这是一个错误。我已经为这个问题提交了a GCC bug report。现在是fixed in GCC's trunk。
解决方法
正如 Revolver_Ocelot 所指出的,&*
似乎强制 g++
执行正确的类型推断。因此,我当前的解决方法(在宏内部采用一些可能是this
的指针表达式)是捕获[ptr = &*(ptr_expr)]
。
为什么会这样?
如上所述,GCC 的 Jason Merrill 已在 GCC 的主干中修复了此问题。他认为this
指针需要在 lambda 捕获中进行特殊处理;具体来说,它被视为不是依赖类型。以前,这种特殊处理适用于[this]
,但不适用于[ptr = this]
。
【讨论】:
如果您使用任意指针,我会使用一元+
而不是 &*
。
@T.C.有什么特别的原因吗?我真的不明白一元 +
在应用于指针时通常会有什么含义,但 &*
很明显的意思是“取消引用然后再次获取地址”。
@T.C.啊。这是完全有道理的,但如果它是一个空指针,我还是有 UB,因为 lambda 使用 ptr->
调用函数。以上是关于GCC 无法使用 init-capture 捕获指向模板类型的“this”指针的主要内容,如果未能解决你的问题,请参考以下文章
code blocks里无法build。有人告诉我要把gcc设成根目录下的\MinGW。但是我完全没明白啥意思