Lambda表达式、共享指针和此类型

Lambda expression, shared pointer and the type of this

本文关键字:类型 指针 共享 表达式 Lambda      更新时间:2023-10-16

考虑以下代码:

#include <memory>
#include <cassert>
struct S: std::enable_shared_from_this<S> {
protected:
    S() = default;
    int bar() { return 42; }
};
struct T: S {
    static std::shared_ptr<T> create() {
        return std::shared_ptr<T>(new T{});
    }
    auto foo() {
        auto ptr = std::static_pointer_cast<T>(shared_from_this());
        return [ptr](){ return ptr->bar(); };
    }
private:
    T() = default;
};
int main() {
    std::shared_ptr<T> ptr = T::create();
    auto lambda = ptr->foo();
    assert(lambda() == 42);
}

上面的代码编译完成。如果方法foo修改如下,则不会:

auto foo() {
    // In this case, there is no explicit cast
    // The type of ptr is no longer std::shared_ptr<T>
    // It is std::shared_ptr<S> instead 
    auto ptr = shared_from_this();
    return [ptr](){ return ptr->bar(); };
}

在这种情况下,代码不再编译(既不使用GCC也不使用clang)。

显然,它将在强制转换后编译(这就是我在第一个示例中所做的),但我希望bar即使在这种情况下也对lambda可见,因为它在其上下文中也是可访问的,也是S接口的一部分。

我怀疑这是由于5.1.5p8,特别是:

lambda表达式的复合语句生成函数调用运算符的函数体[…],但对于[…](确定this[…]的类型和值),复合语句在lambda表达式上下文中被考虑。

事实上,clang返回的错误非常清楚:

main.cpp:8:9:注意:只能访问T 类型对象上的成员

我的推论正确吗
是否是由于上述段落,从而导致this指针的确定类型与共享指针的类型不匹配的问题?

shared_ptr参与游戏的事实让我有点难以理解
老实说,我希望这两个例子都能编译,否则两个都会失败。

看起来您只是违反了受保护访问的基本规则。也就是说,整件事与lambdas或共享指针无关。自古以来就存在的受保护访问的古老规则说,基类的受保护成员只能通过派生类的对象访问。与您上面所说的相反,在您的上下文中,S的受保护成员不能通过S类型的对象访问,但可以通过T类型的对象进行访问。

整个事情可以减少以下简单的例子

struct S
{
protected:
  int i;
};
struct T : S
{
    void foo()
    {
        this->i = 5; // OK, access through `T`
        T t;
        t.i = 5; // OK, access through `T`
        S s;
        s.i = 5; // ERROR: access through `S`, inaccessible
        S *ps = this;
        ps->i = 5; // ERROR: access through `S`, inaccessible
    }
};

我认为问题中的评论已经给出了答案,我不想在这里申请学分。

我想您可能对一种"更好"的方式感兴趣,这种方式可以执行静态强制转换的工作,而无需实际调用静态强制转换,甚至不需要知道基类:

首先定义这个有用的免费函数:

template<class T>
auto shared_from_that(T* p)
{
  return std::shared_ptr<T>(p->shared_from_this(), p);
}

然后获得正确键入的共享指针:

auto foo() {
    return [ptr = shared_from_that(this)](){ 
      return ptr->bar(); 
    };
}

简介:

调用std::shared_ptr的2参数构造函数(似乎广泛未知),该构造函数使用arg1中shared_ptr的控制块和arg2中指向受控对象的指针。