如何约束模板参数以符合std::map中的Key
How do I constrain a template parameter to comply to a Key in std::map?
我有一个类模板,它打算使用它的形参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;
};
这取决于你所说的"服从"是什么意思。如果您想验证K
有<
操作符,那么您可以尝试Boost概念检查库。
#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,那将需要在所有可能的输入下测试运行时行为。
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)
。如果末尾返回类型中的表达式无效,则从重载集中删除test
的这个特定重载。换句话说,它是SFINAE'd。
§14.8.2 [temp.deduct]
p6 在模板实参推导过程中的某些地方,有必要采用使用模板形参的函数类型,并用相应的模板实参替换这些模板形参。当任何显式指定的模板实参被替换为函数类型时,在模板实参演绎开始时执行,当任何从默认实参推导或获得的模板实参被替换时,在模板实参演绎结束时再次执行。
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>;
一个完整的解决方案可能是不可能的。但我可以建议你一个解决方案,如果你想限制K
是一个特定的类型,你可以定义你的类如下
template <class K, bool = std::is_same<K,int>::value>
class Foo { ... };
template <class K>
class Foo<K,false> { Foo(); };
所以如果K
是int
你的类工作如预期。否则你不能构造类型为Foo<K>
的对象。显然你可以改善条件。
要检查K
是否具有operator<
,您可以使用boost::has_less
,但正如您可以在文档中检查的那样,此特征并不总是正常工作。
从你的问题来看,我猜你想要一个合适的错误,而不是大量的错误。
下面的代码查找类型是否有有效的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'
只要你使用了与std::map
不兼容的A
类型,编译器就会报错:
error: aggregate ‘Foo<A> oa’ has incomplete type and cannot be defined
演示- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 使用一个考虑到std::map中键值的滚动或换行的键
- 有没有办法对std::unordered_set、std::unrdered_map、std::set、std::map
- 将重物插入std::map
- 使用通用值初始化 std::map,不重复
- 仅包含可移动 std::map 的类的移动构造函数不起作用
- C++:当所有条目都保证是唯一时,替代 std::map
- 使用模板化的键类型定义 std::map,该键类型基于作为参数接收的函数
- 如果 KEY 是 std::list 或 std::vector 而不是值,那么 std::map 的默认行为是什么?
- C++如何创建 std::map
- 从其他容器中移动构造"std::map"
- 将 std::map::emplace 与返回 shared_ptr 的函数一起使用是否正确?
- C++中 std::map 的运行时复杂度是多少?
- 为什么在 std::map 上移动无法将元素从一个映射移动到另一个映射
- 使用重载 [] 运算符返回 std::map() 的可赋值
- std::map, std::unordered_map - 缩小初始值设定项列表中的转换范围
- C++ 使用枚举类对象分配 std::map 值
- 静态 std::map instatiation 在类的方法中调用构造函数吗?
- std::map:当元素不可默认构造时创建/替换元素
- Arduino编译器和STL:使用std::vector和std::map