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)警告解释会改变;目前它推断出一个初始化列表,但可能以后的版本将(或已经这样做)推断thisin accordance with the new auto rules from N3922的类型。

令我震惊的是 GCC 允许 [ptrthis] 但不允许 [ptr(this)]。我对此没有任何解释。

【问题讨论】:

有趣。请注意,在 GCC 4.9.3 下将其更改为 [ptr = static_cast&lt;const Foo&lt;T&gt;* const&gt;(this)] 对我有效(一旦将 foogetCallableFoo 声明为 const,它们应该是这样)。 @Yuushi 嗯。我希望它可以在宏BIND_MEMBER_TO_THIS 中工作,它只是将任意成员函数绑定到对象的this 指针。我想使用更通用的BIND_MEMBER_TO_OBJ_PTR 宏来实现宏,它会初始化捕获指针,但现在我正在解决(明显的)错误,只需使用[this] 而不是重写整个 lambda利用另一个宏。至于foogetCallableFoo,我在这里尽可能地通用——foo() 可以是const 或非const 请注意,宏不能包含强制转换,除非使用decltype。啊啊啊……看来static_cast&lt;decltype(this)&gt;(this),虽然看起来很傻,但确实有效。我将把它添加到问题中。 如果您使用简单的大括号初始化器而不是=,是否会出现相同的错误? [ptr(this)]() ... 有趣的是,[ptr = &amp;*this] 工作... 【参考方案1】:

这是一个错误。

这是一个错误。我已经为这个问题提交了a GCC bug report。现在是fixed in GCC's trunk。

解决方法

正如 Revolver_Ocelot 所指出的,&amp;* 似乎强制 g++ 执行正确的类型推断。因此,我当前的解决方法(在宏内部采用一些可能是this 的指针表达式)是捕获[ptr = &amp;*(ptr_expr)]

为什么会这样?

如上所述,GCC 的 Jason Merrill 已在 GCC 的主干中修复了此问题。他认为this 指针需要在 lambda 捕获中进行特殊处理;具体来说,它被视为不是依赖类型。以前,这种特殊处理适用于[this],但不适用于[ptr = this]

【讨论】:

如果您使用任意指针,我会使用一元 + 而不是 &amp;* @T.C.有什么特别的原因吗?我真的不明白一元 + 在应用于指针时通常会有什么含义,但 &amp;* 很明显的意思是“取消引用然后再次获取地址”。 @T.C.啊。这是完全有道理的,但如果它是一个空指针,我还是有 UB,因为 lambda 使用 ptr-&gt; 调用函数。

以上是关于GCC 无法使用 init-capture 捕获指向模板类型的“this”指针的主要内容,如果未能解决你的问题,请参考以下文章

未捕获 constexpr 变量

GCC 程序中没有捕获异常

捕获shell输出时,Chef编译错误

code blocks里无法build。有人告诉我要把gcc设成根目录下的\MinGW。但是我完全没明白啥意思

仅捕获 UIView 2 手指 UIPanGestureRecognizer

CMD中无法使用GCC编译C程序