【C++】STL 算法 - transform 变换算法 ③ ( transform 和 for_each 算法的区别 | STL 算法接收的可调用对象分析 - 以 transform 为例进行分析)

2024-01-15 09:50:10 浏览数 (1)

一、transform 和 for_each 算法的区别


1、transform 和 for_each 算法作用区别

for_each 算法 主要用于 对容器中的每个元素执行某种操作 , 而不一定产生新的值或改变原容器的值 , 可能涉及改变元素的值 , 输出元素的值等 ; 使用该算法常用于 遍历输出到控制台 操作 或 修改原容器中的元素内容 操作 ;

transform 算法 主要用于 对容器中的每个元素进行转换 , 并将结果存储到另一个容器中 , 其执行的是一对一的映射操作 ; 会生成新的序列 , 或者在原地修改序列 ;

2、transform 和 for_each 算法 返回值区别

transform 算法 返回一个迭代器 , 指向输出序列的最后一个元素的下一个位置 , 如果提供了 输出迭代器 , 则 transform 不保证 原容器 的内容不变 ;

for_each 算法 返回一个函数对象 , 一般情况下不会使用该返回值 , for_each 的主要目的是执行遍历操作 , 而不是产生新的序列或返回值 ;

3、transform 和 for_each 算法 接收的 函数对象 参数 和 返回值区别

for_each 算法 接收 的 函数对象 的 参数 一般都是 引用参数 , 返回值为 void ;

transform 算法 接收 的 函数对象 的 参数 一般都是 值参数 , 返回值 必须有类型 , 是输出容器元素类型 ;

二、STL 算法接收的可调用对象分析 - 以 transform 为例进行分析


1、参考代码示例

在下面的代码中 ,

首先 , 创建了一个 vector 数组容器 ,

代码语言:javascript复制
	// 创建一个 vector 数组容器
	vector<int> myVector;

	// 向容器中插入元素
	myVector.push_back(9);
	myVector.push_back(5);
	myVector.push_back(2);
	myVector.push_back(7);

然后 , 使用 transform 算法为每个容器中的元素进行自增操作 , 将自增的元素继续输出到 原来的 数组容器中 ;

代码语言:javascript复制
	// 向 transform 变换算法中 传入 Lambda 表达式
	transform(myVector.begin(), myVector.end(), myVector.begin(), [](int element) {
		return   element;
	});

最后 , 使用 for_each 算法遍历 vector 数组容器 , 查看 transform 变换后的结果 ;

代码语言:javascript复制
	// 遍历容器
	for_each(myVector.begin(), myVector.end(), [](int element) {
		cout << element << endl;
	});

代码示例 :

代码语言:javascript复制
#include "iostream"
using namespace std;
#include <vector>
#include <algorithm>
#include "functional"

int main() {

	// 创建一个 vector 数组容器
	vector<int> myVector;

	// 向容器中插入元素
	myVector.push_back(9);
	myVector.push_back(5);
	myVector.push_back(2);
	myVector.push_back(7);

	// 向 transform 变换算法中 传入 Lambda 表达式
	transform(myVector.begin(), myVector.end(), myVector.begin(), [](int element) {
		return   element;
	});

	// 遍历容器
	for_each(myVector.begin(), myVector.end(), [](int element) {
		cout << element << endl;
	});

	// 控制台暂停 , 按任意键继续向后执行
	system("pause");
	return 0;
};

执行结果 :

代码语言:javascript复制
10
6
3
8
请按任意键继续. . .

2、transform 函数接收的规则

将 transform 函数的 Lambda 表达式 的 返回值注释掉 ,

代码语言:javascript复制
	// 向 transform 变换算法中 传入 Lambda 表达式
	transform(myVector.begin(), myVector.end(), myVector.begin(), [](int element) {
		//return   element;
	});

将 接收 int 参数 , 返回 int 返回值 的 Lambda 表达式 ,

代码语言:javascript复制
int(int)

变成了 接收 int 参数 , 返回 void 的 Lambda 表达式 ;

代码语言:javascript复制
void(int)

此时编译时 , 报如下错误 :

代码语言:javascript复制
1>------ 已启动生成: 项目: HelloWorld, 配置: Debug Win32 ------
1>Test.cpp
1>C:Program Files (x86)Microsoft Visual Studio2019CommunityVCToolsMSVC14.24.28314includealgorithm(1310,1): error C2440: “=”: 无法从“void”转换为“int”
1>C:Program Files (x86)Microsoft Visual Studio2019CommunityVCToolsMSVC14.24.28314includealgorithm(1310,24): message : void 类型的表达式不能转换为其他类型
1>Y:02_WorkSpace02_VSHelloWorldHelloWorldTest.cpp(21): message : 查看对正在编译的函数 模板 实例化“_OutIt std::transform<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<_Ty>>>,std::_Vector_iterator<std::_Vector_val<std::_Simple_types<_Ty>>>,main::<lambda_3c270406302a4dd5e2ba2daa1c0faf8f>>(const _InIt,const _InIt,_OutIt,_Fn)”的引用
1>        with
1>        [
1>            _OutIt=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>,
1>            _Ty=int,
1>            _InIt=std::_Vector_iterator<std::_Vector_val<std::_Simple_types<int>>>,
1>            _Fn=main::<lambda_3c270406302a4dd5e2ba2daa1c0faf8f>
1>        ]
1>已完成生成项目“HelloWorld.vcxproj”的操作 - 失败。
========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

3、查看算法函数接收的可调用对象 - 以 transform 函数为例

查看 transform 函数的源码 如下 :

代码语言:javascript复制
// FUNCTION TEMPLATE transform
template <class _InIt, class _OutIt, class _Fn>
_OutIt transform(const _InIt _First, const _InIt _Last, _OutIt _Dest, _Fn _Func) {
    // transform [_First, _Last) with _Func
    _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) {
        *_UDest = _Func(*_UFirst);
    }

    _Seek_wrapped(_Dest, _UDest);
    return _Dest;
}

在上述 transform 源码中 , 传入的可调用对象是 _Fn _Func 参数 , 在代码中 , 会调用该 可调用对象 , 并返回一个值 , 使用 *_UDest 接收返回值 ,

代码语言:javascript复制
    for (; _UFirst != _ULast;   _UFirst, (void)   _UDest) {
        *_UDest = _Func(*_UFirst);
    }

之后 , 还要将该返回值 的 地址 作为参数 , 传递给 _Seek_wrapped 函数 ,

代码语言:javascript复制
_Seek_wrapped(_Dest, _UDest);

这个 _Fn _Func 参数 执行后 , 必须有返回值 ;

0 人点赞