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

Lambda expression, shared pointer and the type of this

考虑以下代码:

#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,特别是:

The lambda-expression's compound-statement yields the function-body [...] of the function call operator, but for [...], determining the type and value of this [...], the compound-statement is considered in the context of the lambda-expression.

其实clang返回的错误已经很清楚了:

main.cpp:8:9: note: can only access this member on an object of type T

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

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

看来您只是违反了受保护访问的基本规则。 IE。整个事情与 lambdas 或共享指针无关。从一开始就存在的受保护访问的古老规则说,base class 的受保护成员只能通过派生 class 的对象访问。与您上面所说的相反,在您的上下文中,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
    }
};

我认为问题中的评论有答案,我不想在这里声称功劳。

我认为您可能对 'nicer' 执行静态转换工作的方式感兴趣,而无需实际调用静态转换,甚至不需要知道基础 class:

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

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 中指向受控对象的指针。