如何发出昂贵的廉价模板参数信号?

时间:2013-04-01 17:34:26

标签: c++ templates c++11 typetraits

我有一个简单bi_map的工作代码,这是一个双向地图类,用于在两个方向上存储相关的键值对。我目前的用法是,NID是某种数字ID或枚举,而OBJ是一个昂贵的不可复制的类对象。

最近,我注意到我还需要bi_map<NID,std::string>std::string是一个便宜的OBJ,应该真的被复制。

什么是对下面的代码进行概括以便用户可以发信号通知某些内容是否昂贵(我想使用指针/引用)或便宜(我想按值复制所有内容)的正确方法是什么,这样我就可以使用正确的实现?

CODE

template<typename NID,typename OBJ>
class bi_map
{
  std::map<NID,OBJ*>        m_nid_2_ptr;
  std::map<OBJ const*,NID>  m_ptr_2_nid;

public:
  void insert( NID nid, OBJ& obj )
  {
    m_nid_2_ptr.insert( std::make_pair( nid, &obj ));
    m_ptr_2_nid.insert( std::make_pair( &obj, nid ));
  }

  NID operator[]( OBJ const& obj ) const
  {
    return m_ptr_2_nid.at( &obj );
  }
  OBJ const& operator[]( NID nid ) const
  {
    return *(m_nid_2_ptr.at( nid ));
  }

  using pairs_cb = std::function<void(NID,OBJ const&)>;
  void pairs( pairs_cb cb ) const
  {
    for( const auto& p : m_nid_2_ptr )
      cb( p.first, *p.second );
  }
  size_t size() const { return m_nid_2_ptr.size(); }
};

2 个答案:

答案 0 :(得分:5)

一般来说,有多种选择,我猜,没有一个正确的答案。所以,让我们尝试找一些适用于的东西。你说你想区分便宜和昂贵的类型。最重要的设计选择是界面。你可以使用:

1)专门针对指针的模板,在使用便宜类型的接口中具有显式线索

bi_map< int, std::string* > bi_map_1; // note * is to flag std::string as cheap
bi_map< int, ExpensiveObject > bi_map_2; // no *, thus using heavy implementation

实现如下:

template< typename NID, typename OBJ >
struct bi_map
{
    // implementation for expensive objects, use OBJ* or std::shared_ptr<OBJ>
};

// specialize of the second parameter is a pointer
template< typename NID, typename OBJ >
struct bi_map< NID, OBJ* >
{
    // implementation for cheap objects, store a copy, i.e., use OBJ
};

当然,如果您发现更具可读性,也可以使用&代替*标记类型。

2)如果希望在界面中显示便宜/昂贵的分隔,即,如果你想

bi_map< int, std::string > bi_map_1; // no *
bi_map< int, ExpensiveObject > bi_map_2; // no *

你需要不同的东西。一种解决方案是添加默认模板参数:

template< typename >
struct is_expensive_for_bi_map : std::false_type {};

template< typename IND, typename OBJ, bool = is_expensive_for_bi_map< OBJ >::value >
struct bi_map
{
    // implementation for expensive objects, use OBJ* or std::shared_ptr<OBJ>
};

template< typename NID, typename OBJ >
struct bi_map< NID, OBJ, false >
{
    // implementation for cheap objects, store a copy, i.e., use OBJ
};

并且对于您认为昂贵的每种类型,您都要添加

template<>
struct is_expensive_for_bi_map< ExpensiveObject > : std::true_type {};

如果昂贵应该是默认值,只需反转名称并调整其余部分,应该很容易。

答案 1 :(得分:0)

另一种选择:

template<typename T>
struct notExpensive {
  static const bool value = FALSE;
  typedef T REF;
  static T& ref(T& x) { return x; }
  static T& deref(T& x) { return x; }
};
template<typename T>
struct isExpensive {
  static const bool value = TRUE;
  typedef T* REF;
  static T* ref(T& x) { return &x; }
  static T deref(T* x) { return x; }
};
template<typename T>
struct expensiveP : public notExpensive<T> {};

// List of expensive types:
template<> struct expensiveP<ExpensiveObject> : public isExpensive<T> {};

然后用昂贵的P调用填充BiMap。