我使用类型擦除(void cast)在一对一地图中存储多个基本类型(float,bool,int等)。重新解释原始数据类型的一种方法是使用pair / union / class来存储具有类型标识符(例如,对)的值。在没有Boost的情况下使用C ++ 11解决没有标识符的基本类型是否有一种干净的方法?
std::map<int, void *> m_mymap;
// avoid std::map<int, pair<void, MyEnum> > m_mymap;
template <class T> void setValue<T>(int i_key, T i_val)
{
m_mymap[i_key] = reinterprete_cast<void *>(i_val);
}
template <class T> T getValue<T>(int i_key)
{
return reinterprete_cast<T>(i_val);
}
doSomeWork()
{
for (const auto & elem : m_mymap)
{
auto val = some_magical_cast< ??? >(elem.second) // resolve type without a nasty switch against MyEnum
// do some work
}
}
编辑:更新问题以使用void *
答案 0 :(得分:3)
auto val = some_magical_cast< ??? >(elem.second) // resolve type without a nasty switch against MyEnum
C ++是一种静态类型语言。必须在编译时知道每种类型。您尝试做的事情将要求根据放入地图的内容在运行时确定类型。
用C ++中的任何东西都不可能。 val
必须具有单个特定类型,而不是通过执行运行时代码确定的类型。循环的每次迭代都必须赋予它相同的类型。 union或variant
允许您存储在运行时确定的几种类型之一。但是它们存储的类型集在编译时是固定的。
即使auto val
可能以某种方式导致一系列不同的类型,void*
也不包含任何可用于恢复其指向的类型的信息。请注意,boost/std::any
也缺乏恢复此类型的能力;你必须要求一个特定的类型(不同的是,如果你要求错误的类型,any
将失败,而void*
将给你UB)。
所以不,这是不可能的。
答案 1 :(得分:2)
正如@NicolBolas正确解释的那样,你不能。
作为一种可行的方法/解决方法,您可以使用基于双重调度理念的技术,如下例所示:
#include<map>
#include<utility>
#include<iostream>
class C {
public:
template<typename T>
void accept(int k, T v) { std::cout << k << " " << v << std::endl; }
};
using P = void(*)(C &, int, void *);
std::map<int, std::pair<P, void*>> m;
template<typename T>
void proto(C &c, int k, void *v) {
c.accept(k, *static_cast<T*>(v));
}
template <class T>
void set(int k, T *v) {
m[k] = { &proto<T>, v };
}
void get(C &c, int k) {
auto p = m[k];
p.first(c, k, p.second);
}
int main() {
int i = 42;
double d = 1.2;
set(0, &i);
set(1, &d);
C c;
for(auto &&k: m) {
get(c, k.first);
}
}
它需要一个额外的类(示例中为C
)
您可以为该类添加不同类型的不同方法(例如,您可以使用两种接受int
或double
的方法代替模板方法。
这样,您可以拥有广义行为以及许多特定类型的行为。
当然,它更复杂而且不是免费的,但是否则你无法获得你想要的东西。