在CRTP中对shared_ptr的隐式向下转换

时间:2019-07-20 09:57:14

标签: c++ shared-ptr implicit-conversion crtp

我建立了一个与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_;}
};

2 个答案:

答案 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的事实。当然,当我构建客户时,我需要了解所有这些信息,但这并不意味着其他人也需要了解这些信息。但是,从您的评论中,我觉得我正在搞砸。