1

何でもマッピングできる完全に汎用的なマッピングを C++ で定義したいと思います。

std::map を試しましたが、プリミティブまたはオブジェクト (キーとして) を他のプリミティブまたはオブジェクト (値として) にマップできるように、K と V を十分に一般化するにはどうすればよいでしょうか。

または、使用できる別のメカニズムはありますか?

編集: 明確にするために、クラスに任意のデータをアタッチできるようにする基本クラス (すべてのクラスが派生するクラス) で関係を定義しようとしています。最も単純なアプローチは、上記のキーが文字列である名前と値のペアです。もっと一般的なことをするかどうか疑問に思っていましたか?

4

3 に答える 3

6

不可能です-そうあるべきです。キーまたは値のいずれかの意味のある動作に依存することはできず、「何でも」全体で意味のあるバイナリ関係またはハッシュ関数を設計することは不可能であるため、このようなマッピングは価値がありません。タイプなので、可能な領域にさえほど遠いです。

編集: 防止するものは何もありませんstd::unordered_map<std::string, boost::any>- または実際には、boost::anyたまたまstd::unordered_mapいくつかの型の

しかし、あなたのデザインは非常に疑わしいようです。基本的に、明らかな利点がないためにコンパイラを完全に破壊しています。すべてのクラスを共通のベースから派生させるのはなぜですか? そして、いったいなぜ任意のデータを添付したいのでしょうか? クラスにデータを配置する通常の方法は、クラスに配置することです。C++ を強制的にインタープリター言語にしようとして、安全性とパフォーマンスと正気をすべて吹き飛ばしてはいけません。

于 2012-09-01T06:55:06.803 に答える
1

それは可能です-したがって、この点では@DeadMGに同意しません。

それは無価値です - この点では完全に同意します。

しかし、私はその答えの概念を理解していません。つまり、「しないでください」と答える代わりに、「この方法で行うことができますが、私のアドバイスはしないでください」ということです。私は「人生の先生」のふりをしているわけではありません - 私はただ答えているだけです.

値については、boost::any のようなものを使用します。

キーの場合 - std::map がキーの順序を定義するため、より複雑です。したがって、汎用キーは次の規則に従う必要があります。

  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;

上記のオブジェクトは、任意のものを指すことができます。ただし、それがどのタイプであるかを示す何かも必要であるため、文字列タイプです。

次に、型の内容を読み取って、ポインターを必要な型にキャストできるようにする必要があります。

例えば。

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 に答える