C++ STL源码分析——算法(一)

【侯捷-SL体系结构内核分析-算法】

目录:
accumulate
for_each
replace, replace_if, replace_copy

accumulate

源码

accumulate 的源码如下。

template<class _InIt,
	class _Ty,
	class _Fn>
	_NODISCARD inline _Ty accumulate(const _InIt _First, const _InIt _Last, _Ty _Val, _Fn _Reduce_op)
	{	// return noncommutative and nonassociative reduction of _Val and all in [_First, _Last), using _Reduce_op
	_Adl_verify_range(_First, _Last);
	auto _UFirst = _Get_unwrapped(_First);
	const auto _ULast = _Get_unwrapped(_Last);
	for (; _UFirst != _ULast; ++_UFirst)
		{
		_Val = _Reduce_op(_Val, *_UFirst);
		}

	return (_Val);
	}
template<class _InIt,
	class _Ty>
	_NODISCARD inline _Ty accumulate(const _InIt _First, const _InIt _Last, _Ty _Val)
	{	// return noncommutative and nonassociative reduction of _Val and all in [_First, _Last)
	return (_STD accumulate(_First, _Last, _Val, plus<>()));
	}
  • 它有两个重载函数,第一个函数参数为:容器的 begin iterator、容器的 end iterator、计算初值、仿函数或者函数指针。计算初始和容器元素依据传入的函数方法 _Reduce_op 进行相应累计操作。如下行代码:
_Val = _Reduce_op(_Val, *_UFirst);
  • 第二个函数参数则将函数操作指定为 plus<>(),即对初值和容器数据进行默认累加操作。
测试代码
int myFunc(int x, int y)
{
	return x + 2 * y;
}

struct myClass
{
	int operator()(int x, int y)
	{
		return x + 3 * y;
	}
}myObj;


void test_accumulate()
{
	int nInit = 100;
	int data[3] = { 10, 20, 30 };

	int result = std::accumulate(data, data + 3, nInit);
	// 100 + 10 + 20 + 30 = 160
	std::cout << result << endl;

	result = std::accumulate(data, data + 3, nInit, myFunc);
	// 100 + 2 * 10 + 2 * 20 + 2 * 30 = 220
	std::cout << result << endl;

	result = std::accumulate(data, data + 3, nInit, myObj);
	// 100 + 3 * 10 + 3 * 20 + 3 * 30 = 280
	std::cout << result << endl;
}
注意点
  • accumulate算法和其他算法不一样,它位于 numeric 文件中。
  • 请看如下代码:
void test_accumulate()
{
	int nInit = 0;
	float data[3] = { 10.3, 20.4, 30.5 };

	float result = std::accumulate(data, data + 3, nInit);
	std::cout << result << endl;
}

正确的输出应该是 61.2,但是实际输出为 60,这是为什么呢?
可以往前看看 accumulate 的源代码,可以看到,计算结果的类型会根据传入初值参数类型进行判断,与容器数值类型并没有关系,由于这里传入初值 nInit 为整形,所以每次累加时都会转换成整型类型,最后输出60。如果将 nInit 改为和容器数据类型相同浮点类型,就可以得出正确结果。

for_each

源码
template<class _InIt,
	class _Fn> inline
	_Fn for_each(_InIt _First, _InIt _Last, _Fn _Func)
	{	// perform function for each element [_First, _Last)
	_Adl_verify_range(_First, _Last);
	auto _UFirst = _Get_unwrapped(_First);
	const auto _ULast = _Get_unwrapped(_Last);
	for (; _UFirst != _ULast; ++_UFirst)
		{
		_Func(*_UFirst);
		}

	return (_Func);
	}
  • 它遍历容器中 [ _First, _Last) 区间的数据,并对每个数据进行 _Func函数操作。类似于C++ 11引进的新操作 range_based for statement:
	vector<int> myVec = vector<int>{ 0, 1, 2, 3,4 };
	for (auto i : myVec)
	{
		cout << i << " ";
	}

replace, replace_if, replace_copy

replace 源码
template<class _FwdIt,
	class _Ty> inline
	void replace(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Oldval, const _Ty& _Newval)
	{	// replace each matching _Oldval with _Newval
	_Adl_verify_range(_First, _Last);
	auto _UFirst = _Get_unwrapped(_First);
	const auto _ULast = _Get_unwrapped(_Last);
	for (; _UFirst != _ULast; ++_UFirst)
		{
		if (*_UFirst == _Oldval)
			{
			*_UFirst = _Newval;
			}
		}
	}
  • 它遍历容器中的每一个元素,当当前遍历的元素与传入的 _Oldval 相等时,将该 _Oldval 替换成为 _Newval。
replace_if 源码
template<class _FwdIt,
	class _Pr,
	class _Ty> inline
	void replace_if(const _FwdIt _First, const _FwdIt _Last, _Pr _Pred, const _Ty& _Val)
	{	// replace each satisfying _Pred with _Val
	_Adl_verify_range(_First, _Last);
	auto _UFirst = _Get_unwrapped(_First);
	const auto _ULast = _Get_unwrapped(_Last);
	for (; _UFirst != _ULast; ++_UFirst)
		{
		if (_Pred(*_UFirst))
			{
			*_UFirst = _Val;
			}
		}
	}
  • replace_if 在 replace 的基础上,会多传入一个_Pred参数,用于判断替换条件。也就是代码中将 _UFirst == _Oldval 判断条件改为 _Pred(_UFirst) 条件。
replace_copy源码
template<class _InIt,
	class _OutIt,
	class _Ty> inline
	_OutIt replace_copy(_InIt _First, _InIt _Last,
		_OutIt _Dest, const _Ty& _Oldval, const _Ty& _Newval)
	{	// copy replacing each matching _Oldval with _Newval
	_Adl_verify_range(_First, _Last);
	auto _UFirst = _Get_unwrapped(_First);
	const auto _ULast = _Get_unwrapped(_Last);
	auto _UDest = _Get_unwrapped_n(_Dest, _Idl_distance<_InIt>(_UFirst, _ULast));
	for (; _UFirst != _ULast; ++_UFirst, (void)++_UDest)
		{
		if (*_UFirst == _Oldval)
			{
			*_UDest = _Newval;
			}
		else
			{
			*_UDest = *_UFirst;
			}
		}

	_Seek_wrapped(_Dest, _UDest);
	return (_Dest);
	}
  • 不同于replace 和 replace_if 会修改源容器的值,replace_copy 会遍历源容器的元素,当源容器元素与 _Oldval 相等时,则会以新值 _Newval 复制到目标容器相应位置,否则则会复制源容器元素值。

你可能感兴趣的:(编程语言,#,C++)