请考虑以下代码:
#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-expression的复合语句产生函数调用操作符的函数体[...],但对于[...],确定 this 的类型和值[。 ..],复合语句在lambda表达式的上下文中被考虑。
事实上,clang返回的错误非常清楚:
main.cpp:8:9:注意:只能在
T
类型的对象上访问此成员
我的扣除权是否合适?
是由于提到的段落,因此this
指针的确定类型的问题与共享指针的一个不匹配?
shared_ptr
参与游戏的事实让我有点难以理解。
老实说,我希望这两个例子都能编译,否则两者都会失败。
答案 0 :(得分:3)
看起来您只是违反了受保护访问的基本规则。即整个事情与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
}
};
答案 1 :(得分:3)
我认为问题中的评论有答案,我不打算在这里申请。
我认为您可能对执行静态强制转换工作的'更好'方式感兴趣,而不实际调用静态强制转换甚至需要知道基类:
首先定义这个有用的自由函数:
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中受控对象的指针。