如何将变量模板模拟为类成员?

时间:2013-05-18 15:27:13

标签: c++

我想就如何将变量模板模拟为类成员发表意见。这样,您的类中的数据成员依赖于模板,但不依赖于类模板。从概念上讲,可以写成:

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

但我真的不喜欢它,即使可以通过重复使用未使用的整数或使用更合适的容器来改进它。

我想在这里可能有更好的解决方案,这就是我要问的原因。

2 个答案:

答案 0 :(得分: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  

但是,如果您正在使用boost,则可以大大简化:

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

答案 1 :(得分: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