Я помещаю экземпляры шаблонного класса, содержащего разнородные значения шаблона, в контейнер, например 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;
}
std::vector
однороден по определению (даже если его тип значения является вариантом, который стирает фактическое содержимое по типу). Таким образом, вы не можете вытащить тип обратно. Единственный разнородный контейнер (или контейнер, поскольку он не соответствует требованиямContainer
) - этоstd::tuple
. - person Quentin   schedule 28.06.2017