1

我想在 C++ 中定义一个完全通用的映射,我可以将任何东西映射到任何东西。

我尝试了 std::map 但是 K 和 V 应该是什么才能使其足够通用,以便我可以将基元或对象(作为键)映射到其他基元或对象(作为值)。

还是我可以使用另一种机制?

编辑:为了澄清起见,我试图在基类中定义一个关系(我的所有类都从该类派生),这将允许我将任意数据附加到我的类。最简单的方法是一个名称-值对,其中上面的键是一个字符串。我想知道我是否做一些更通用的事情?

4

3 回答 3

6

不可能——因为它应该是。这样的映射将毫无价值,因为您不能依赖键或值的任何有意义的行为,并且不可能设计对“任何东西”有意义的二进制关系或散列函数,或者可以对任何东西进行操作类型,所以它甚至远不及可能的领域。

编辑:没有什么可以阻止std::unordered_map<std::string, boost::any>- 或者确实,boost::any它恰好持有std::unordered_map某些类型的 a 。

但是,您的设计似乎很有问题。你基本上完全颠覆了编译器,没有明显的好处。为什么你要从一个共同的基础派生每个类?你到底为什么要附加任意数据?将数据放入类中的常用方法是将其放入类中,而不是试图强迫 C++ 成为一种解释性语言,从而破坏你所有的安全、性能和理智。

于 2012-09-01T06:55:06.803 回答
1

这是可能的 - 所以在这一点上我不同意@DeadMG。

这是毫无价值的——在这一点上完全同意,

但是我不理解回答的概念,我的意思是“不要这样做”,而是回答“可以以这种方式完成,但我的建议是不要这样做”。我不假装自己是“生活老师”——我只是在回答,

对于值 - 使用类似 boost::any 的东西。

对于键 - 它更复杂 - 因为 std::map 定义了键中的顺序。所以通用键必须遵循ss规则:

  1. 如果真实键类型相同 - 使用真实键的顺序
  2. 如果真正的键不相同 - 您必须定义类型之间的顺序(如 typeinfo::name() 的顺序)
  3. 通用密钥必须是可复制构造的

让我们看看我对密钥的建议(使用类型擦除):

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 }; };

ideone上的完整示例 另见本文

于 2012-09-01T11:27:35.790 回答
-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"

因此调用的函数是由对象的实际(真实)类型定义的。

于 2012-09-01T06:55:06.630 回答