从异构模板化值的容器中返回模板化类型

时间:2017-06-28 14:12:19

标签: c++ templates boost template-meta-programming

我将包含异构模板值的模板化类的实例放入容器中,例如std :: vector。当从所述向量访问特定元素时,我要求返回精确模板化类型,而不仅仅是基指针WrapperBase*。我已经涉及了boost :: any,boost :: variant和基类/模板化的子类,如下所示。编译时解决方案是理想的(也许是一些元模板魔法);否则,基于运行时RTTI的解决方案就足够了。

在一个完美的世界里,我可以做这样的事情:

vector< WrapperBase > v;
v.push_back( Wrapper<int>{1} );
v.push_back( Wrapper<float>{3.14f} );
auto v = vector[0].get(); //typeid( decltype( v) ).name() would be "int"
auto v = vector[1].get(); //typeid( decltype( v) ).name() would be "float"

Boost变体几乎提供了解决方案;但是,我只能访问operator()()方法中的特定类型,并且无法返回它。该类型将被其他函数使用,因此我无法使访问者方法执行所需的工作。

此代码可用http://coliru.stacked-crooked.com/a/5a44681322cdbd77

#include <iostream>
#include <type_traits>
#include <vector>
#include <boost/variant.hpp>
#include <boost/any.hpp>

struct WrapperBase
{
   template< typename T >
   T* get();
};

template< typename T >
struct Wrapper : WrapperBase
{
   Wrapper( T* t ) : WrapperBase(), _val( t ) {}
   T* get() { return _val; }
   T* _val;
};

template< typename T >
T* WrapperBase::get()
{
   return static_cast<Wrapper<T>>( this )->get();
}

struct Visitor : boost::static_visitor<int> //sadly, we can only return ints :(.
{
   int operator()( Wrapper<int>& b ) const
   {
      auto bType = *b.get();
      std::cout << "b type is " << typeid( decltype( bType ) ).name() << "... Val is " << bType << "...\n";
      return 2;
   }

   int operator()( Wrapper<float>& b ) const
   {
      auto bType = *b.get();
      std::cout << "b type is " << typeid( decltype( bType ) ).name() << "... Val is " << bType << "...\n";
      return 6;
   }

   int operator()( Wrapper<std::string>& b ) const
   {
      auto bType = *b.get();
      std::cout << "b type is " << typeid( decltype( bType ) ).name() << "... Val is " << bType << "...\n";
      return 12345;
   }

};

int main( int argc, char* args[] )
{
   std::cout << "Hello World...\n";

   int ia = 5;
   Wrapper<int> a{ &ia };

   float fb = 3.1415f;
   Wrapper<float> b{ &fb };

   std::string s = "testing this...\n";
   Wrapper<std::string> c{ &s };

   auto aType = *a.get(); //aType is "int"
   std::cout << "a type is " << typeid( decltype( aType ) ).name() << "... Val is " << aType << "...\n";

   auto bType = *b.get(); //aType is "float"
   std::cout << "b type is " << typeid( decltype( bType ) ).name() << "... Val is " << bType << "...\n";

   auto cType = *c.get(); //aType is "std::string"
   std::cout << "c type is " << typeid( decltype( cType ) ).name() << "... Val is " << cType << "...\n";

   std::vector< boost::any > vec;

   vec.push_back( &a );
   vec.push_back( &b );
   vec.push_back( &c );

   //for( int i = 0; i < vec.size() ; i++ )
   //{
   //In my actual code, I have no way of knowing I need to cast to <int> and therefore cannot cast.
   //   auto t = boost::any_cast<Wrapper<int>*>( vec[i] );
   //   std::cout << "[" << i << "] type is " << typeid( decltype( t ) ).name() << "... Val is " << t << "...\n";    
   //}    
   std::cout << "...\n...\n...\n";

   using VAR = boost::variant< Wrapper<int>, Wrapper<float>, Wrapper<std::string> >;

   std::vector< VAR > myVar;
   myVar.push_back( a );
   myVar.push_back( b );
   myVar.push_back( c );
   for (int i = 0; i < myVar.size() ; i++)
   {
      auto v = myVar[i];
      //apply_visitor is VERY close to what I want - I have precise type, but only within operator()()
      std::cout << boost::apply_visitor( Visitor(), v ) << "\n";
   }

   std::cout << "...\n...\n...\n";

   std::vector< WrapperBase > v;
   v.push_back( a );
   v.push_back( b );
   v.push_back( c );

   //for (int i = 0; i < v.size() ; i++)
   //{
   //   auto t = v[i].get<int>(); //same problem as boost::any, but instead I have pointer to base class
   //   std::cout << "["<<i<<"] type is " << typeid( decltype( t )).name() << "... Val is " << t << "...\n";
   //   
   //}


   return 0;
}

1 个答案:

答案 0 :(得分:2)

这篇精彩的文章讨论了如何将访问者模式与类型删除的基础对象(如std::any https://gieseanw.wordpress.com/2017/05/03/a-true-heterogeneous-container-in-c/一起使用,所以如果你使用那里的概念,你可以在理论上使用访客模式与同质容器。

您获取此类代码的目标

// typeid(val_one).name() is 
auto val_one = homogenous_container[runtime_value_one]; 
// typeid(val_two).name() is `double`
auto val_two = homogenous_container[runtime_value_two]; 

无法实现,auto类型推导在编译时发生,没有办法让它绑定到运行时条件并根据它改变类型。

您最接近的是使用std::variant并使用访客模式。您不能将编译时推断类型绑定到任意运行时类型。它们只是不同的概念,并且在C ++编译/运行时的不同时间存在。

类型擦除将允许您实现这样的一些功能,但它要求您的容器由共享一些公共接口的类型组成。例如,在std::function的情况下,可以根据std::function

的模板参数的签名来调用可以擦除类型的类型。