有没有办法通过引用模拟向下转换

时间:2015-01-29 20:35:14

标签: c++ templates c++11 casting

所以,我有这些结构的一些东西:

struct Generic {}
struct Specific : Generic {}

在某些时候我需要向下转,即:

Specific s = (Specific) GetGenericData();

这是一个问题,因为我收到错误消息,指出没有用户定义的强制转换可用。

我可以将代码更改为:

Specific s = (*(Specific *)&GetGenericData())

或使用reinterpret_cast,它将是:

Specific s = *reinterpret_cast<Specific *>(&GetGenericData());

但是,有没有办法让这个更干净?也许使用宏或模板?

我查看了这篇文章C++ covariant templates,我认为它有一些相似之处,但不知道如何根据我的情况重写它。我真的不想把事物定义为SmartPtr。我宁愿把事物当成它们的对象。

4 个答案:

答案 0 :(得分:6)

从您的使用情况来看,GetGenericData()似乎返回Generic个值,在这种情况下,由于object slicing,对Specific的强制转换将不安全。

要做你想做的事,你应该让它返回指针或引用:

Generic* GetGenericData();
Generic& GetGenericDataRef();

然后你可以进行演员表演:

// safe, returns nullptr if it's not actually a Specific*
auto safe = dynamic_cast<Specific*>(GetGenericData());

// for references, this will throw std::bad_cast
// if you try the wrong type
auto& safe_ref = dynamic_cast<Specific&>(GetGenericDataRef());

// unsafe, undefined behavior if it's the wrong type,
// but faster if it is
auto unsafe = static_cast<Specific*>(GetGenericData());

答案 1 :(得分:1)

我在这里假设您的数据很简单。

struct Generic {
  int x=0;
  int y=0;
};
struct Specific:Generic{
  int z=0;
  explicit Specific(Generic const&o):Generic(o){}

  // boilerplate, some may not be needed, but good habit:
  Specific()=default;
  Specific(Specific const&)=default;
  Specific(Specific &&)=default;
  Specific& operator=(Specific const&)=default;
  Specific& operator=(Specific &&)=default;
};

鲍勃是你的叔叔。 int z有一个默认的初始化程序,这有点重要,所以我们不必在来自父母的ctor中重复它。

我明确指出了它,因此只会明确地调用它,而不是偶然地调用它。

这是简单数据的合适解决方案。

答案 2 :(得分:0)

所以第一步是要意识到你有一个动态的状态问题。您存储的状态的性质会根据动态信息而变化。

struct GenericState { virtual ~GenericState() {} }; // data in here
struct Generic;
template<class D>
struct GenericBase {
  D& self() { return *static_cast<D&>(*this); }
  D const& self() const { return *static_cast<D&>(*this); }
  // code to interact with GenericState here via self().pImpl
  // if you have `virtual` behavior, have a non-virtual method forward to
  // a `virtual` method in GenericState.
};

struct Generic:GenericBase<Generic> {
  // ctors go here, creates a GenericState in the pImpl below, or whatever
  ~GenericState() {} // not virtual
private:
  friend struct GenericBase<Generic>;
  std::unique_ptr<GenericState> pImpl;
};
struct SpecificState : GenericState {
  // specific stuff in here, including possible virtual method overrides
};

struct Specific : GenericBase<Specific> {
  // different ctors, creates a SpecificState in a pImpl
  // upcast operators:
  operator Generic() && { /* move pImpl into return value */ }
  operator Generic() const& { /* copy pImpl into return value */ }
private:
  friend struct GenericBase<Specific>;
  std::unique_ptr<SpecificState> pImpl;
};

如果您希望能够复制,请在virtual GenericState* clone() const中实施GenericState方法,并在SpecificState中覆盖它。

我在这里所做的是将类型规范化(或者如果我们不支持移动则半规则化)。 Specific和Generic类型是不相关的,但它们的后端实现细节(GenericState和SpecificState)是相关的。

主要通过CRTP和GenericBase来避免接口重复。

现在,向下转换可能涉及动态检查。你通过pImpl并将其抛弃。如果在rvalue上下文中完成,它将移动 - 如果在左值上下文中,则复制。

如果您愿意,可以使用共享指针而不是唯一指针。这将允许非复制非移动的投射。

答案 3 :(得分:0)

好的,经过一些额外的研究,我想知道这样做有什么问题:

struct Generic {}
struct Specific : Generic {
    Specific( const Generic &obj ) : Generic(obj) {}
}

如果我错了,请纠正我,但这可以使用隐式复制构造函数。

假设是这种情况,我可以避免写一个并自动执行转换,现在我可以写:

Specific s = GetGenericData();

当然,对于大型物体,这可能不是一个好主意,但对于较小的物体,这将是一个正确的&#34;溶液