我想在 C++ 中定义一个完全通用的映射,我可以将任何东西映射到任何东西。
我尝试了 std::map 但是 K 和 V 应该是什么才能使其足够通用,以便我可以将基元或对象(作为键)映射到其他基元或对象(作为值)。
还是我可以使用另一种机制?
编辑:为了澄清起见,我试图在基类中定义一个关系(我的所有类都从该类派生),这将允许我将任意数据附加到我的类。最简单的方法是一个名称-值对,其中上面的键是一个字符串。我想知道我是否做一些更通用的事情?
不可能——因为它应该是。这样的映射将毫无价值,因为您不能依赖键或值的任何有意义的行为,并且不可能设计对“任何东西”有意义的二进制关系或散列函数,或者可以对任何东西进行操作类型,所以它甚至远不及可能的领域。
编辑:没有什么可以阻止std::unordered_map<std::string, boost::any>
- 或者确实,boost::any
它恰好持有std::unordered_map
某些类型的 a 。
但是,您的设计似乎很有问题。你基本上完全颠覆了编译器,没有明显的好处。为什么你要从一个共同的基础派生每个类?你到底为什么要附加任意数据?将数据放入类中的常用方法是将其放入类中,而不是试图强迫 C++ 成为一种解释性语言,从而破坏你所有的安全、性能和理智。
这是可能的 - 所以在这一点上我不同意@DeadMG。
这是毫无价值的——在这一点上完全同意,
但是我不理解回答的概念,我的意思是“不要这样做”,而是回答“可以以这种方式完成,但我的建议是不要这样做”。我不假装自己是“生活老师”——我只是在回答,
对于值 - 使用类似 boost::any 的东西。
对于键 - 它更复杂 - 因为 std::map 定义了键中的顺序。所以通用键必须遵循ss规则:
让我们看看我对密钥的建议(使用类型擦除):
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 }; };
您需要将 K 和 V 设为特殊对象。
该对象将需要包括它是什么对象类型。
struct {
void *pointer;
string type;
// int type; // this is also possible
} Object;
上面的 Object 可以指向任何东西。但是,它还需要说明它是什么类型,因此需要类型字符串。
然后,您需要能够通过读取类型中的内容将指针转换回所需的类型。
例如。
if (type == "int") cout << (int*)(myobject.pointer) << endl;
无论如何,如果你做这样的事情,你几乎开始构建一个松散类型的解释器,因为对于你想要对对象执行的任何操作,你都需要检查它的类型(无论你是添加、连接还是打印值到标准输出)。
如果您使用类对象并使用继承来存储您需要的任何数据,这可能会更好。
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"
因此调用的函数是由对象的实际(真实)类型定义的。