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

kfm*_*e04 2 c++ templates constraints stdmap c++11

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

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

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

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

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

  private:
    std::map< K, size_t > m_map;
};
Run Code Online (Sandbox Code Playgroud)

And*_*ard 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;
};
Run Code Online (Sandbox Code Playgroud)

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


Xeo*_*Xeo 5

C++11版本:

\n
#include <type_traits>\n\ntemplate<class T>\nstruct satisfies_key_req{\n  struct nat{};\n\n  template<class K> static auto test(K* k) -> decltype(*k < *k);\n  template<class K> static nat  test(...);\n\n  static bool const value = !std::is_same<decltype(test<T>(0)), nat>::value;\n};\n\n#include <iostream>\n\nstruct foo{};\n\nint main(){\n    static bool const b = satisfies_key_req<int>::value;\n    std::cout << b << '\\n';\n    static bool const b2 = satisfies_key_req<foo>::value;\n    std::cout << b2 << '\\n';\n}\n
Run Code Online (Sandbox Code Playgroud)\n

输出:

\n
1\n0\n
Run Code Online (Sandbox Code Playgroud)\n
\n

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

\n

\xc2\xa714.8.2 [temp.deduct]

\n
\n

p6在模板实参推导过程中的某些时刻,需要采用使用模板实参的函数类型,并将这些模板实参替换为相应的模板实参。当任何显式指定的模板参数被替换到函数类型中时,这是在模板参数推导开始时完成的,并且当从默认参数推导或获得的任何模板参数被替换时,在模板参数推导结束时再次完成

\n

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

\n

p8 如果替换导致无效类型或表达式,则类型推导失败。无效类型或表达式是指如果使用替换参数编写的类型或表达式,其格式会不正确。[...]

\n
\n
\n

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

\n
// static_assert, arguably the best choice\ntemplate< typename K >\nclass Foo\n{\n  static_assert<satisfies_key_req<K>::value, "K does not satisfy key requirements");\n  // lots of other code here...\n\n  private:\n    std::map< K, size_t > m_map;\n};\n\n// new-style SFINAE'd, maybe not really clear\ntemplate<\n  typename K,\n  typename = typename std::enable_if<\n               satisfies_key_req<K>::value\n             >::type\n>\nclass Foo\n{\n  // lots of other code here...\n\n  private:\n    std::map< K, size_t > m_map;\n};\n\n// partial specialization, clarity similar to SFINAE approach\ntemplate< \n  typename K,\n  bool = satisfies_key_req<K>::value\n>\nclass Foo\n{\n  // lots of other code here...\n\n  private:\n    std::map< K, size_t > m_map;\n};\n\ntemplate<typename K>\nclass Foo<K, false>;\n
Run Code Online (Sandbox Code Playgroud)\n