多载化决议是在STL(Standard Library)实现中的一种技术,例如有多个同名函数实现相似的功能,甚至参数列表难以区分(考虑模板函数)。要区分这些函数使得编译通过,修改函数名不是一种好方法,更好的方法是设置过个标记,增加一个参数接收标记,通过标记来决定用哪个函数。代码示例如下
#include<iostream>
using namespace std;
struct T1{};
struct T2 :T1 {};
struct T3 {};
void f(T1)
{
cout << "T1\n";
return;
}
/*
void f(T2)
{
cout << "T2\n";
return;
}
*/
void f(T3)
{
cout << "T3\n";
return;
}
int main()
{
f(T2());
f(T3());
return 0;
}
这里会输出
T1
T3
这里T2继承T1,所以对T2的调用转到了对T1的调用。但是如果把注释取消,则会输出T2.
在/bits/stl_iterator_base_types.h中有如下tag定义。
struct input_iterator_tag { };
/// Marking output iterators.
struct output_iterator_tag { };
/// Forward iterators support a superset of input iterator operations.
struct forward_iterator_tag : public input_iterator_tag { };
/// Bidirectional iterators support a superset of forward iterator
/// operations.
struct bidirectional_iterator_tag : public forward_iterator_tag { };
/// Random-access iterators support a superset of bidirectional
/// iterator operations.
struct random_access_iterator_tag : public bidirectional_iterator_tag { };
在STL中定义迭代器的前进函数需要多载化决议技术,先看一些宏,在MingGW\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\concept_check.h中
#ifndef _CONCEPT_CHECK_H
#define _CONCEPT_CHECK_H 1
#pragma GCC system_header
#include <bits/c++config.h>
#if !defined(_GLIBCXX_CONCEPT_CHECKS) || !_GLIBCXX_HOSTED
#define __glibcxx_function_requires(...)
#define __glibcxx_class_requires(_a,_b)
#define __glibcxx_class_requires2(_a,_b,_c)
#define __glibcxx_class_requires3(_a,_b,_c,_d)
#define __glibcxx_class_requires4(_a,_b,_c,_d,_e)
#else // the checks are on
#include <bits/boost_concept_check.h>
#define __glibcxx_function_requires(...) \
__gnu_cxx::__function_requires< __gnu_cxx::__VA_ARGS__ >();
#define __glibcxx_class_requires(_a,_C) \
_GLIBCXX_CLASS_REQUIRES(_a, __gnu_cxx, _C);
#define __glibcxx_class_requires2(_a,_b,_C) \
_GLIBCXX_CLASS_REQUIRES2(_a, _b, __gnu_cxx, _C);
#define __glibcxx_class_requires3(_a,_b,_c,_C) \
_GLIBCXX_CLASS_REQUIRES3(_a, _b, _c, __gnu_cxx, _C);
#define __glibcxx_class_requires4(_a,_b,_c,_d,_C) \
_GLIBCXX_CLASS_REQUIRES4(_a, _b, _c, _d, __gnu_cxx, _C);
#endif // enable/disable
#endif // _GLIBCXX_CONCEPT_CHECK
在我的配置中上面的宏都是空. MingGW\mingw64\lib\gcc\x86_64-w64-mingw32\8.1.0\include\c++\bits\stl_iterator_base_funcs.h中有如下代码
template<typename _InputIterator, typename _Distance>
inline _GLIBCXX14_CONSTEXPR void
__advance(_InputIterator& __i, _Distance __n, input_iterator_tag)
{
// concept requirements
__glibcxx_function_requires(_InputIteratorConcept<_InputIterator>)
__glibcxx_assert(__n >= 0);
while (__n--)
++__i;
}
template<typename _BidirectionalIterator, typename _Distance>
inline _GLIBCXX14_CONSTEXPR void
__advance(_BidirectionalIterator& __i, _Distance __n,
bidirectional_iterator_tag)
{
// concept requirements
__glibcxx_function_requires(_BidirectionalIteratorConcept<
_BidirectionalIterator>)
if (__n > 0)
while (__n--)
++__i;
else
while (__n++)
--__i;
}
template<typename _RandomAccessIterator, typename _Distance>
inline _GLIBCXX14_CONSTEXPR void
__advance(_RandomAccessIterator& __i, _Distance __n,
random_access_iterator_tag)
{
// concept requirements
__glibcxx_function_requires(_RandomAccessIteratorConcept<
_RandomAccessIterator>)
if (__builtin_constant_p(__n) && __n == 1)
++__i;
else if (__builtin_constant_p(__n) && __n == -1)
--__i;
else
__i += __n;
}
3个同名模板函数依靠tag参数区分,都是使迭代器移动n个位置。_InputIterator只能每次前进1个单位,_BidirectionalIterator每次可以前进或后退1个单位,_RandomAccessIterator则可以随机访问。使用advance函数时当然只有参数__i, __n是必要的,期望tag参数能自动判断。这个自动判断机制叫做traits,就是提取类型的特性。这个traits机制也是用模板类实现,并且用到了partial specialization,其实就是某些特殊情况特殊处理。
template<typename _Iterator, typename = __void_t<>>
struct __iterator_traits { };
template<typename _Iterator>
struct __iterator_traits<_Iterator,
__void_t<typename _Iterator::iterator_category,
typename _Iterator::value_type,
typename _Iterator::difference_type,
typename _Iterator::pointer,
typename _Iterator::reference>>
{
typedef typename _Iterator::iterator_category iterator_category;
typedef typename _Iterator::value_type value_type;
typedef typename _Iterator::difference_type difference_type;
typedef typename _Iterator::pointer pointer;
typedef typename _Iterator::reference reference;
};
template<typename _Iterator>
struct iterator_traits
: public __iterator_traits<_Iterator> { };
/// Partial specialization for pointer types.
template<typename _Tp>
struct iterator_traits<_Tp*>
{
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef _Tp& reference;
};
/// Partial specialization for const pointer types.
template<typename _Tp>
struct iterator_traits<const _Tp*>
{
typedef random_access_iterator_tag iterator_category;
typedef _Tp value_type;
typedef ptrdiff_t difference_type;
typedef const _Tp* pointer;
typedef const _Tp& reference;
};
__void_t<...>定义为void,所以前面一大段都可以忽略。制定STL迭代器的时候都应该指定iterator_category; value_type; difference_type; pointer; reference;
这些不是变量,对编译器来说只是名称的代换。而基本类型像int没有这5个名词的定义,但是它的迭代器如int *会根据partial specialization得到这5个名词。
最后把__advance函数包装起来,
template<typename _InputIterator, typename _Distance>
inline _GLIBCXX17_CONSTEXPR void
advance(_InputIterator& __i, _Distance __n)
{
// concept requirements -- taken care of in __advance
typename iterator_traits<_InputIterator>::difference_type __d = __n;
std::__advance(__i, __d, std::__iterator_category(__i));
}