3

変数テンプレートをクラス メンバーとしてエミュレートする方法についてご意見をお聞かせください。これは、テンプレートに依存しているが、クラス テンプレートには依存していないクラス内のデータ メンバーを持つことです。概念的には、次のように記述できます。

class M
{
private:
    template<typename T>
    SomethingThatCouldDependOnT<T> m_dataMember;
};

私の最初の考えは、次のようなものを作ることでした:

#include <iostream>

class A
{
public :
    template<typename T>
    void myMethod() 
    { 
        static int a = 0;
        std::cout << "my method : " << a << " calls" << std::endl;
        a++;
    }
};


int main()
{
    A a, b;
    a.myMethod<int>();
    a.myMethod<int>();
    a.myMethod<double>();
    b.myMethod<double>();
    return 0;
}

しかし、myMethod の静的メンバーはインスタンスごとではなく、生成されたメソッドごとであるため、これは機能しません。実際には、 myMethod は最初のパラメーターとして型 A のオブジェクトを取るグローバル関数と見なすことができるため (可視性の問題を考慮せずに)、これは理にかなっています。だから私は別のアイデアを思いついた:

#include <iostream>
#include <vector>

class A
{
public :
    A() : i(-1) {}
    template<typename T>
    void myMethod() 
    { 
        static std::vector<int> a;
        static int max = 0;
        if(i < 0)
        {
            i = max;
            a.push_back(0);
            max++;
        }
        std::cout << "my method : " << a[i] << " calls" << std::endl;
        a[i]++;
    }
private:
    int i;
};


int main()
{
    A a, b;
    a.myMethod<int>();
    a.myMethod<int>();
    a.myMethod<double>();
    b.myMethod<double>();
    return 0;
}

しかし、未使用の整数を再利用したり、より適切なコンテナーを使用したりすることで改善できたとしても、私はそれがあまり好きではありません。

ここで私の問題に対するより良い解決策があるかもしれないと考えたいので、私は尋ねています。

4

2 に答える 2

4

これを行うには、type_info ポインターから型固有のデータへのマップを作成します。

次に例を示します。

#include <iostream>
#include <map>
#include <typeinfo>

// Custom comparison operator that uses the std::type_info::before member
// function.  Comparing the pointers doesn't work since there is no
// guarantee that the typeid operator always gives you the same object
// for the same type.
struct BeforeType {
  bool operator()(const std::type_info *a,const std::type_info *b) const
  {
    return a->before(*b);
  }
};

struct A {
  template <typename T>
  int &member()
  {
    return member_map[&typeid(T)];
  }

  std::map<const std::type_info *,int,BeforeType> member_map;
};

int main(int,char**)
{
  A a1, a2;
  ++a1.member<int>();
  ++a1.member<int>();
  ++a1.member<double>();
  ++a2.member<int>();
  std::cout << a1.member<int>() << "\n";
  std::cout << a1.member<double>() << "\n";
  std::cout << a1.member<float>() << "\n";
  std::cout << a2.member<int>() << "\n";
  return 0;
}

出力は次のとおりです。

2
1
0
1

さまざまなタイプの値のコンテナに興味がある場合は、次のようなものを使用できます。

#include <iostream>
#include <map>
#include <typeinfo>


struct BeforeType {
  bool operator()(const std::type_info *a,const std::type_info *b) const
  {
    return a->before(*b);
  }
};

struct Value {
  virtual ~Value() { }
  virtual Value *clone() = 0;
};

template <typename T>
struct BasicValue : Value {
  T value;
  BasicValue() : value() { }
  BasicValue(const T &value) : value(value) { }
  virtual Value *clone() { return new BasicValue(value); }
};

struct TypeMap {
  TypeMap() { }

  TypeMap(const TypeMap &that)
  {
    add(that.value_map);
  }

  template <typename T>
  T &value()
  {
    ValueMap::iterator iter = value_map.find(&typeid(T));
    if (iter==value_map.end()) {
      BasicValue<T> *member_ptr = new BasicValue<T>;
      value_map.insert(ValueMap::value_type(&typeid(T),member_ptr));
      return member_ptr->value;
    }
    return static_cast<BasicValue<T> *>(iter->second)->value;
  }

  TypeMap &operator=(const TypeMap &that)
  {
    clear();
    add(that.value_map);
    return *this;
  }

  void clear()
  {
    while (!value_map.empty()) {
      Value *member_ptr = value_map.begin()->second;
      value_map.erase(value_map.begin());
      delete member_ptr;
    }
  }

  ~TypeMap()
  {
    clear();
  }

  private:    
    typedef std::map<const std::type_info *,Value *,BeforeType> ValueMap;
    ValueMap value_map;

    void add(const ValueMap &value_map)
    {
      ValueMap::const_iterator iter = value_map.begin(), end = value_map.end();
      for (;iter!=end;++iter) {
        this->value_map[iter->first] = iter->second->clone();
      }
    }
};

int main(int,char**)
{
  TypeMap type_map;
  type_map.value<int>() = 5;
  type_map.value<float>() = 2.5;
  type_map.value<std::string>() = "hi";
  std::cout << type_map.value<int>() << "\n";
  std::cout << type_map.value<float>() << "\n";
  std::cout << type_map.value<std::string>() << "\n";
  return 0;
}

出力は次のとおりです。

5                                                                                      
2.5                                                                                    
hi  

ただし、ブーストを使用している場合、これは大幅に簡素化できます。

struct TypeMap {
  template <typename T>
  T &value()
  {
    boost::any &any_value = value_map[&typeid(T)];
    if (any_value.empty()) {
      any_value = T();
    }
    return *boost::any_cast<T>(&any_value);
  }

  private:
    std::map<const std::type_info *,boost::any,BeforeType> value_map;
};

C++11 では、以下を使用してカスタム比較を取り除くこともできますstd::type_index

struct TypeMap {
  template <typename T>
  T &value()
  {
    boost::any &any_value = value_map[std::type_index(typeid(T))];
    if (any_value.empty()) {
      any_value = T();
    }
    return *boost::any_cast<T>(&any_value);
  }

  private:
    std::map<const std::type_index,boost::any> value_map;
};
于 2013-05-18T15:53:06.973 に答える
1

必要なのは、静的メンバーを持つ追加のテンプレートだけです。

#include <iostream>

template<class T>
struct StaticForMyMethod
{
    static int value;
};

template<class T>
int StaticForMyMethod<T>::value;

template<typename T>
void myMethod()
{
    int& a = StaticForMyMethod<T>::value;
    std::cout << "my method : " << a << " calls" << std::endl;
    a++;
}

int main() {
    myMethod<int>();
    myMethod<int>();
    myMethod<long>();
    myMethod<long>();
}

出力:

my method : 0 calls
my method : 1 calls
my method : 0 calls
my method : 1 calls
于 2013-05-20T10:34:32.043 に答える