我想在c ++中定义一个完全通用的映射,我可以将任何东西映射到任何东西。
我尝试了std :: map但是K和V应该是什么才能使它足够通用,这样我就可以将基元或对象(作为键)映射到其他基元或对象(作为值)。
或者我可以使用其他机制吗?
编辑:为了澄清,我试图在基类中定义一个关系(从中派生我的所有类),这将允许我将任意数据附加到我的类。最简单的方法是一个名称 - 值对,其中上面的键是一个字符串。我想知道我是否做了更通用的事情?
答案 0 :(得分:6)
不可能 - 应该如此。这样的映射将毫无价值,因为您不能依赖于任何键或值的任何有意义的行为,并且不可能设计二元关系或散列函数,这些函数在“任何东西”中都是有意义的,或者可以在任何操作上运行类型,所以它甚至远不及可能的领域。
编辑:没有什么可以阻止std::unordered_map<std::string, boost::any>
- 或者确实,boost::any
恰好拥有某些类型的std::unordered_map
。
但是,您的设计似乎非常值得怀疑。你基本上完全颠覆了编译器没有明显的好处。你为什么要从一个共同的基础派生每一个班级?为什么你想要附加任意数据呢?将数据放入类中的通常方法是将它放在类中,而不是通过强迫C ++成为一种解释语言来打击你所有的安全性,性能和理智。
答案 1 :(得分:1)
有可能 - 所以在这一点上我不同意@DeadMG。
这是毫无价值的 - 在这一点上完全同意,
但是我不明白回答的概念,我的意思是“不要做”答案“它可以这样做,但我的建议是不要这样做”。我不假装成“生活老师” - 我只是在回答,
对于值 - 使用类似boost :: any的东西。
对于键 - 它更复杂 - 因为std :: map定义键中的顺序。因此,通用密钥必须遵循以下规则:
让我看看我的密钥提议(使用类型擦除):
template <typename T>
struct GenKeyTypeOrder;
class GenKeyImplInt {
public:
// true if before other Key in other
virtual bool before(const GenKeyImplInt&) const = 0;
// type value
virtual int typeOrder() const = 0;
virtual GenKeyImplInt* clone() const = 0;
virtual ~GenKeyImplInt() {}
};
template <typename RealKey>
class GenKeyImpl : public GenKeyImplInt {
public:
GenKeyImpl(RealKey realKey) : realKey(realKey) {}
// true if before other Key in other
virtual bool before(const GenKeyImplInt& r) const
{
const GenKeyImpl* rp = dynamic_cast<const GenKeyImpl*>(&r);
if (rp) return realKey < rp->realKey;
return typeOrder() < r.typeOrder();
}
// type value
virtual int typeOrder() const { return GenKeyTypeOrder<RealKey>::VALUE; }
virtual GenKeyImpl* clone() const { return new GenKeyImpl(*this); }
private:
RealKey realKey;
};
class GenKey {
public:
// true if before other Key in other
friend bool operator < (const GenKey& l, const GenKey& r)
{
return l.impl->before(*r.impl);
}
template <typename T>
GenKey(T t) : impl(new GenKeyImpl<T>(t)) {}
GenKey(const GenKey& oth) : impl(oth.impl->clone()) {}
~GenKey() { delete impl; }
private:
GenKey& operator = (const GenKey& oth); // not defined
GenKeyImplInt* impl;
};
// define for every type you want be used as generic key
template <>
struct GenKeyTypeOrder<int> { enum { VALUE = 0 }; };
template <>
struct GenKeyTypeOrder<std::string> { enum { VALUE = 1 }; };
答案 2 :(得分:-1)
您需要将K和V作为特殊对象。
对象需要包含它的对象类型。
struct {
void *pointer;
string type;
// int type; // this is also possible
} Object;
上面的对象可以指向任何东西。但是,它还需要说明它是什么类型,因此是类型字符串。
然后你需要能够通过读取类型中的内容将指针强制转换回所需的类型。
例如
if (type == "int") cout << (int*)(myobject.pointer) << endl;
无论如何,如果你做这样的事情,你几乎开始构建一个松散类型的解释器,因为对于你想要对象执行的任何操作,你需要检查它的类型(是否要添加,连接或者将值打印到stdout)。
如果使用类对象,并使用继承来存储所需的任何数据,可能会更好。
class Object {
public virtual string to_string() {
return "";
}
};
然后,如果你想存储一个整数:
class Integer : public Object {
int i;
public string to_string() {
char str[50];
sprintf(str,"%d",i);
return string(str);
}
public Integer operator=(int a) {
i=a;
return this;
}
};
通过这种方式,您可以定义希望所有对象都支持的所有函数的接口。
请注意,使基础Object类具有虚函数意味着如果您说:
Integer a;
a=5;
Object object = (Object)a;
cout << object.to_string << endl; // prints "5"
因此调用的函数是由对象的实际(true)类型定义的。