C++中用于存储特定模板对象的通用容器

3
我正在尝试创建一个通用的容器,可以存储Wrapper< T>类型的异构对象,其中T可以是任何用户定义的类型。我已经看过boost::any和其他解决方案,但是如果不将其重新转换回原始类型,就无法调用函数foo()(我不知道要重新转换哪种类型,关于T的信息丢失了)。
如何合理地实现一个通用容器/使用现有的通用容器来实现这一点?
template <typename T>
class Wrapper{
public:
  Wrapper(const T& a):o(a){};
  Wrapper(){};
  //public methods
  void foo(){
     //do stuff
  };
private:
 T o;
};

class X{};
class Y{};

int main(){
  X x;
  Y y;

  A_GENERIC_CONTAINER generic_container;
  // A_GENERIC_CONTAINER should be able to store
  // any number of heterogeneous objects of type Wrapper<T>
  // where T can be any user defined type.

  generic_container.push_back(x);
  generic_container.push_back(y);

  auto it =  generic_container.begin();
  auto end =  generic_container.end();
  while(it != end){
    it->foo();
    ++it;
  }
}

也许这会有所帮助。这是来自创始人本人的网址:http://www.stroustrup.com/bs_faq2.html#containers - Nikos C.
谢谢Nikos,但问题是我不能使用任何通用类的向量 - Wrapper<T1>和Wrapper<T2>是完全不同的两个类 - 所以没有运行时多态性。 - biswa.panda
如果我将其放置到(void *)中,则必须将其重新转换为Wrapper <T>以使用foo,其中我已经丢失了有关T的信息。 - biswa.panda
vector<boost::variant<T1,T2..TN> > 或 boost::fusion::vector<T1,T2..TN> > 可以解决这个问题,但仅适用于子集 [即类型 T1、T2.. TN]。 - biswa.panda
你能创建一个虚拟基类BaseWrapper,然后让Wrapper<T1>和Wrapper<T2>定义实现吗?那么你需要以一种通用的方式暴露多少个函数? - Dave S
Boost::Variant + 访问者模式通常是人们解决这类问题的方法。 - Riga
1个回答

5
最通用的方法是创建一个Wrapper的基类,比如BaseWrapper,并在BaseWrapper上定义纯虚函数,然后在每个Wrapper类上实现这些纯虚函数。通过特化,每个Wrapper可以有自己的实现。一旦完成,您可以使用智能指针容器来使用Base Type,并随意使用它。
还有一些可能的捷径。例如,在您的示例中的简单情况下,我建议使用std::function。帮助函数有助于做到这一点。请注意,这只适用于仅有1个要调用的方法的情况。我还建议直接定义您的包装器的operator(),以便您不必使用绑定。
template<T>
std::function<void()> wrap(const T& x)
{
   return std::bind(&Wrapper<T>::foo, Wrapper<T>(x));
}

int main(){
  X x;
  Y y;

  std::vector<std::function<void()> > generic_container;
  // A_GENERIC_CONTAINER should be able to store
  // any number of heterogeneous objects of type Wrapper<T>
  // where T can be any user defined type.

  generic_container.push_back(wrap(x));
  generic_container.push_back(wrap(y));

  auto it =  generic_container.begin();
  auto end =  generic_container.end();
  while(it != end){
    (*it)();
    ++it;
  }
}

编辑:

我讨论的是一个非模板化的基类,从中您可以派生出所有的模板包装器。这样,您就可以调用您预定义的方法(必须由包装器实现),而不需要知道涉及的具体包装器类型。

class Base
{
  public:
   virtual ~Base() {};
   virtual void foo() = 0;
};

template <typename T>
class Wrapper : public Base{
public:
  Wrapper(const T& a):o(a){};
  Wrapper(){};
  //public methods
  virtual void foo(){
     //do stuff
  };
private:
 T o;
};

int main(){
  X x;
  Y y;

  std::vector<std::shared_ptr<Base> > generic_container;
  // A_GENERIC_CONTAINER should be able to store
  // any number of heterogeneous objects of type Wrapper<T>
  // where T can be any user defined type.

  generic_container.push_back(std::make_shared<Wrapper<X>>(x));
  generic_container.push_back(std::make_shared<Wrapper<Y>>(y));

  auto it =  generic_container.begin();
  auto end =  generic_container.end();
  while(it != end){
    it->foo();
    ++it;
  }
}

感谢 Dave。你的第二个快捷方式解决了我的问题。但我想要理解你的第一个解决方案,它使用了一个 BaseWrapper 类。 - biswa.panda
我该如何创建一个智能指针容器来包装基类的封装类? vector<shared_ptr<Base<T> > > wvec; - biswa.panda
@biswa.panda:请看我的编辑。如果需要的话,您可以向基类和包装器中添加更多函数。如果您需要每个包装器超过1个函数,则这可能是最佳方法。 - Dave S
如果需要对某些对象进行细粒度控制以销毁它们,例如批量销毁它们时,可以使用类似这样的方法来实现死亡行模式。我喜欢它!谢谢! - Mihai Todor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接