如何约束模板参数以符合std :: map中的Key?

时间:2011-12-18 02:25:30

标签: c++ templates c++11 constraints stdmap

我有一个类模板,打算使用其参数K作为地图的关键。

有没有办法将模板参数限制为符合std :: map中的Key的类型?

我意识到即使没有这样的约束,编译器也会吐出一堆模板错误,比如没有operator < ()的K,但如果我能在指定需求时使代码更明显,那就太好了。

欢迎使用C ++ 11解决方案。

template< typename K >
class Foo
{
  // lots of other code here...

  private:
    std::map< K, size_t > m_map;
};

4 个答案:

答案 0 :(得分:6)

这取决于你所说的“遵守”。如果您想验证K是否有<运算符,那么您可以尝试使用Boost Concept Checking Library

#include "boost/concept_check.hpp"

template< typename K >
class Foo
{
  BOOST_CONCEPT_ASSERT((boost::LessThanComparable< K >));

  // lots of other code here...

  private:
    std::map< K, size_t > m_map;
};

但是,如果要验证<是否在K上定义了StrictWeakOrdering,则需要在所有可能的输入下测试运行时行为。

答案 1 :(得分:4)

C ++ 11版本:

#include <type_traits>

template<class T>
struct satisfies_key_req{
  struct nat{};

  template<class K> static auto test(K* k) -> decltype(*k < *k);
  template<class K> static nat  test(...);

  static bool const value = !std::is_same<decltype(test<T>(0)), nat>::value;
};

#include <iostream>

struct foo{};

int main(){
    static bool const b = satisfies_key_req<int>::value;
    std::cout << b << '\n';
    static bool const b2 = satisfies_key_req<foo>::value;
    std::cout << b2 << '\n';
}

输出:

1
0

我在这里使用的关键点是表达式SFINAE auto test(K* k) -> decltype(*k < *k)。如果trailing-return-type中的表达式无效,则从重载集中删除test的特定重载。换句话说,它是SFINAE'd。

§14.8.2 [temp.deduct]

  

在模板参数推导过程中的某些点,必须采用一个使用模板参数的函数类型,并将这些模板参数替换为相应的模板参数。这是在当任何显式指定的模板参数被替换为函数类型时模板参数推导的开始,并且当从默认参数推导或获得的任何模板参数被替换时,在模板参数推导结束时再次。 / p>      

p7 替换发生在函数类型和模板参数声明中使用的所有类型和表达式中。 表达式不仅包括常量表达式,例如出现在数组边界中的表达式,还包括非类型模板参数,还包括通用表达式(即非常量表达式) sizeof decltype 以及其他允许使用非常量表达式的上下文。

     

p8如果替换导致无效的类型或表达式,则类型推导失败。如果使用替换参数写入,则无效的类型或表达式将是格式错误的。 [...]


您可以在Foo类中以三种方式使用它来触发错误。

// static_assert, arguably the best choice
template< typename K >
class Foo
{
  static_assert<satisfies_key_req<K>::value, "K does not satisfy key requirements");
  // lots of other code here...

  private:
    std::map< K, size_t > m_map;
};

// new-style SFINAE'd, maybe not really clear
template<
  typename K,
  typename = typename std::enable_if<
               satisfies_key_req<K>::value
             >::type
>
class Foo
{
  // lots of other code here...

  private:
    std::map< K, size_t > m_map;
};

// partial specialization, clarity similar to SFINAE approach
template< 
  typename K,
  bool = satisfies_key_req<K>::value
>
class Foo
{
  // lots of other code here...

  private:
    std::map< K, size_t > m_map;
};

template<typename K>
class Foo<K, false>;

答案 2 :(得分:2)

您的问题的完整解决方案可能是不可能的。但我可以建议您一个解决方案,如果您想将K限制为特定类型,您可以按如下方式定义您的类

template <class K, bool = std::is_same<K,int>::value>
class Foo { ... };

template <class K>
class Foo<K,false> { Foo(); };

因此,如果Kint,则您的课程按预期工作。否则,您无法构造Foo<K>类型的对象。显然你可以改善这种状况。

要检查K是否有operator<您可以使用boost::has_less,但是因为您可以检查documentation此特征无法正常使用。

答案 3 :(得分:1)

从你的问题来看,我想你想要一个合适的错误而不是错误。

以下代码找到,如果某个类型的有效operator <

namespace OperatorExist
{
  typedef char no[3]; // '3' can be any awkward number
  template<typename T> no& operator < (const T&, const T&);

  template<typename T>
  struct LessThan {
    static const bool value = (sizeof(*(T*)(0) < *(T*)(0)) != sizeof(no));
  };
}

现在,您可以使用上述构造专门化class Foo

template<typename K, bool = OperatorExist::LessThan<K>::value>
class Foo 
{
  // lots of other code here...
  private:
    std::map<K, size_t> m_map;
};

template<typename K>
class Foo<K, false>; // unimplemented for types not suitable for 'std::map'

只要您使用与A不兼容的某种类型std::map,编译器就会抱怨单个错误:

error: aggregate ‘Foo<A> oa’ has incomplete type and cannot be defined

Demo