仿函数
所谓仿函数也就是函数对象, 以前是这样称呼它的, 只是一直沿用至今了。
仿函数就是一种具有函数特质的对象.。
可以将部分操作由用户自己来定义然后传入自定义的函数名就可以被调用。
为什么不用函数指针
- 函数指针不能满足STL的抽象性
- 函数指针不能与STL的其他组件搭配, 不够灵活
- 仿函数具有可配接性, 可以满足traits编程, 也是能在编译期间就能完成, 不会有运行时的开销
根据参数个数分类
一元仿函数
一元仿函数基类:
template <class Arg, class Result>
struct unary_function {
typedef Arg argument_type; // 参数类型别名
typedef Result result_type; // 返回值类型别名
};
二元仿函数
二元仿函数基类:
template <class Arg1, class Arg2, class Result>
struct binary_function {
typedef Arg1 first_argument_type; // 参数类型别名
typedef Arg2 second_argument_type; // 参数类型别名
typedef Result result_type; // 返回值类型别名
};
根据功能分类
算术运算
如加法:
template <class T>
struct plus : public binary_function<T, T, T> {
T operator()(const T& x, const T& y) const { return x + y; }
};
关系运算
如等于:
template <class T>
struct equal_to : public binary_function<T, T, bool> {
bool operator()(const T& x, const T& y) const { return x == y; }
};
逻辑运算
如与:
template <class T>
struct logical_and : public binary_function<T, T, bool> {
bool operator()(const T& x, const T& y) const { return x && y; }
};
正同,选择,投射
- 正同 : 任何数值调用该函数都不会有改变
template <class T>
struct identity : public unary_function<T, T> {
const T& operator()(const T& x) const { return x; }
};
- 选择 : 接受一个pair类型, 并且返回第一个元素或者第二个元素
// 选择第一个元素
template <class Pair>
struct select1st : public unary_function<Pair, typename Pair::first_type> {
const typename Pair::first_type& operator()(const Pair& x) const
{
return x.first;
}
};
// 选择第二个元素
template <class Pair>
struct select2nd : public unary_function<Pair, typename Pair::second_type> {
const typename Pair::second_type& operator()(const Pair& x) const
{
return x.second;
}
};
- 投影 : 接受一个pair类型, 忽略第二个元素返回第一个元素或者忽略第一个元素返回第二个元素
// 忽略第二个元素返回第一个元素
template <class Arg1, class Arg2>
struct project1st : public binary_function<Arg1, Arg2, Arg1> {
Arg1 operator()(const Arg1& x, const Arg2&) const { return x; }
};
// 忽略第一个元素返回第二个元素
template <class Arg1, class Arg2>
struct project2nd : public binary_function<Arg1, Arg2, Arg2> {
Arg2 operator()(const Arg1&, const Arg2& y) const { return y; }
};
MyTinySTL仿函数
涉及到的文件functional.h。
代码和上面介绍的一模一样,我就不贴了。
文件中还有个哈希函数对象,对于整型类型可以看到都是返回原值。
template <class Key>
struct hash {};
/*! 哈希函数对象 针对指针的偏特化版本 返回指针的值 */
template <class T>
struct hash<T*>
{
size_t operator()(T* p) const noexcept
{
return reinterpret_cast<size_t>(p);
}
};
/*! 哈希函数对象 对于整型类型,只是返回原值 */
#define MYSTL_TRIVIAL_HASH_FCN(Type) \
template <> struct hash<Type> \
{ \
size_t operator()(Type val) const noexcept \
{ return static_cast<size_t>(val); } \
};
MYSTL_TRIVIAL_HASH_FCN(bool)
MYSTL_TRIVIAL_HASH_FCN(char)
MYSTL_TRIVIAL_HASH_FCN(signed char)
MYSTL_TRIVIAL_HASH_FCN(unsigned char)
MYSTL_TRIVIAL_HASH_FCN(wchar_t)
MYSTL_TRIVIAL_HASH_FCN(char16_t)
MYSTL_TRIVIAL_HASH_FCN(char32_t)
MYSTL_TRIVIAL_HASH_FCN(short)
MYSTL_TRIVIAL_HASH_FCN(unsigned short)
MYSTL_TRIVIAL_HASH_FCN(int)
MYSTL_TRIVIAL_HASH_FCN(unsigned int)
MYSTL_TRIVIAL_HASH_FCN(long)
MYSTL_TRIVIAL_HASH_FCN(unsigned long)
MYSTL_TRIVIAL_HASH_FCN(long long)
MYSTL_TRIVIAL_HASH_FCN(unsigned long long)
#undef MYSTL_TRIVIAL_HASH_FCN
对于浮点类型是逐位哈希。
/**
* @brief 逐位哈希
* @param[in] first 数据地址
* @param[in] count 数据位数
*/
inline size_t bitwise_hash(const unsigned char* first, size_t count)
{
#if (_MSC_VER && _WIN64) || ((__GNUC__ || __clang__) &&__SIZEOF_POINTER__ == 8)
const size_t fnv_offset = 14695981039346656037ull;
const size_t fnv_prime = 1099511628211ull;
#else
const size_t fnv_offset = 2166136261u;
const size_t fnv_prime = 16777619u;
#endif
size_t result = fnv_offset;
for (size_t i = 0; i < count; ++i)
{
result ^= (size_t)first[i];
result *= fnv_prime;
}
return result;
}
/*! 哈希函数对象 对于浮点数,逐位哈希 */
template <>
struct hash<float>
{
size_t operator()(const float& val)
{
return val == 0.0f ? 0 : bitwise_hash((const unsigned char*)&val, sizeof(float));
}
};
/*! 哈希函数对象 对于浮点数,逐位哈希 */
template <>
struct hash<double>
{
size_t operator()(const double& val)
{
return val == 0.0f ? 0 : bitwise_hash((const unsigned char*)&val, sizeof(double));
}
};
/*! 哈希函数对象 对于浮点数,逐位哈希 */
template <>
struct hash<long double>
{
size_t operator()(const long double& val)
{
return val == 0.0f ? 0 : bitwise_hash((const unsigned char*)&val, sizeof(long double));
}
};
配接器
主要用来修改接口。就像转接口一样, 将一个对class封装变成了另外一个功能的class。
STL 主要提供如下三种配接器:
- 改变仿函数(functors)接口,称之为 function adapter
- 改变容器(containers)接口,称之位 container adapter
- 改变迭代器(iterators)接口者,称之为 iterator adapter