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 複製到目標容器相應位置,否則則會複製源容器元素值。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章