我建立了一个与CRTP一起使用的类接口,以实现静态多态性,并为Client类提供了与该接口的shared_ptr。我想从客户端将shared_ptr返回到实现,我可以(安全地?)通过客户端内的static_pointer_cast进行此操作。有没有办法允许从shared_ptr隐式向下转换到Interface,再到从shared_ptr隐式转换到Implementation?
template<class Implementation>
struct Interface {
int foo() { return static_cast<Implementation*>(this)->fooimpl();}
};
template<class Implementation>
struct Client {
Client(std::shared_ptr<Implementation> const & pt) : pt_{pt} {}
std::shared_ptr<Interface<Implementation>> pt_;
std::shared_ptr<Implementation> getPt() {
//Can I avoid this static_pointer_cast?<
return std::static_pointer_cast<Implementation>(pt_);
}
};
一种避免所有这些混乱的可能解决方案是在Client类中保留一个shared_ptr到Implementation。但是,通过这种方式,我无处不在说客户端中的实现具有接口。
template<class Implementation>
struct Client {
Client(std::shared_ptr<Implementation> const & pt) : pt_{pt} {}
std::shared_ptr<Implementation> pt_;
std::shared_ptr<Implementation> getPt() { return pt_;}
};
答案 0 :(得分:0)
是否有一种方法可以允许从shared_ptr隐式向下转换到Interface,再到从shared_ptr隐式转换到Implementation?
简单答案:不!由于编译器不了解“反向”继承,因此可以为您提供直接支持。 CRTP是解决潜在问题的一般模式。这里的下调是手工编码的,但隐藏在CRTP实现的接口后面。
CRTP是因为我希望客户端使用foo()并同时独立于实现
当您将其作为编译时实现实现时,它目前并不是真正独立的。如果您要指向该CRTP类型的内容,则会看到最新的内容。
shared_ptr是因为实现可能在客户端之间共享
您的想法有一个循环问题!
如果您按照示例中的说明进行书写:
模板 struct客户{ 客户(std :: shared_ptr const和pt):pt_ {pt} {} std :: shared_ptr pt_; std :: shared_ptr getPt(){return pt_;} };
调用getPt()
的代码必须知道返回的指针的类型!即使您使用auto
,也将获得返回的指针的类型。因此,您根本无法将其隐藏在使用的代码中。
我最终只是将shared_ptr作为类成员放入了Implementation中,并添加了“ static_assert + is_base_of”来确保这一点。
似乎也是一个循环问题。
如果您写:
template < typename T>
class CRTP: public T
{
public:
void Check()
{
static_assert( std::is_base_of_v < T, CRTP<T> > );
}
};
class A {};
int main()
{
CRTP<A> x;
x.Check();
}
断言在这里有什么帮助?仅检查您在“类CRTP:public T”上方写了4行。对我而言,这毫无意义。
除了使用CRTP之外,我仍然不知道您想要实现什么。
答案 1 :(得分:0)
简单答案:不!由于编译器不了解“反向”继承,因此可以为您提供直接支持。 CRTP是解决潜在问题的一般模式。这里的下调是手工编码的,但隐藏在CRTP实现的接口后面。
正如您所说,CRTP是解决此问题的一种方法。例如,我可以在接口中对强制转换进行编码:
template<class Impl>
struct Interface {
explicit operator Impl const &() const {return static_cast<Impl const &>(*this);}
};
例如,这将帮助我在Client中实现两种复制:
template<class Impl>
struct Client {
Client(shared_ptr<Interface<Impl>> const & pt) : pt_(pt) {}
shared_ptr<Interface<Impl>> const & getPt() const {return pt_;}
shared_ptr<Interface<Impl>> pt_;
Client deepcopy() {
return Client(make_shared<Impl>(*pt_));
}
Client copy() {
return Client(pt_);
}
};
我的问题是关于在接口中编码类似的强制转换,从而允许向下转换shared_ptr。
当您将其作为编译时实现实现时,它目前并不是真正独立的。如果您要指向该CRTP类型的内容,则会看到最新的内容。
我不确定此注释是否正确。当然,它不是独立的,但是Client中的编码应该只知道Interface的方法和成员,而忽略了实现:与动态接口一样。对我来说,不同之处在于,使用CRTP时,毫无疑问是谁实现的,并且向下转换始终是安全的。我可能是错的。
调用getPt()的代码必须知道返回的指针的类型!即使使用auto,您也将获得返回的指针的类型。因此,您根本无法将其隐藏在使用的代码中。
我不是要隐藏类型。我只想与实施一起工作。在Client上下文中对Interface的升级只是为了明确声明实现具有Interface并确保Client使用的接口没有超出Interface允许的范围。
如果实施是:
Implementation : public Interface<Implementation> {
int buffer;
int fooimpl() {return buffer;}
};
我不希望客户端看到“缓冲区”,只是希望它可以得到“ int”。在客户端外部,我可能想更改“缓冲区”的值以使用另一个值再次调用客户端。
断言在这里有什么帮助?仅检查您在“类CRTP:public T”上方写了4行。对我而言,这毫无意义。
断言不在CRTP类型中,而是在客户端中。我的最终实现是:
template<class Impl>
struct Client {
Client(shared_ptr<Impl> const & pt) : pt_(pt) {}
shared_ptr<Impl> const & getPt() const {return pt_;}
static_assert(is_base_of<Interface<Impl>, Impl>::value, "Impl must derive from Interface");
shared_ptr<Impl> pt_;
};
但是,通过这种方式,Client中的开发人员可以在Client中使用Implementation的“缓冲区”,并且代码可以正常编译,直到以OtherImplementation作为模板参数调用Client。
除了使用CRTP之外,我仍然不知道您想要实现什么。
我想我只想隐藏客户端在上下文中使用CRTP的事实。当然,当我构建客户时,我需要了解所有这些信息,但这并不意味着其他人也需要了解这些信息。但是,从您的评论中,我觉得我正在搞砸。