一、std::transform简介
定义于头文件 <algorithm>,std::transform在指定的范围内应用于给定的操作,并将结果存储在指定的另一个范围内。
transform用于转换和结合元素
1.1 算法提供两个形式
(1)一元操作
一元操作有4个参数,把源容器的元素转换到目标区,即元素的复制与修改一气呵成。
对于一元操作,将op应用于[first1, last1]范围内的每个元素,并将每个操作返回的值存储在以result开头的范围内。给定的op将被连续调用last1-first1+1次。op可以是函数指针或函数对象或lambda表达式。
template < class InputIterator, class OutputIterator, class UnaryOperator >
OutputIterator transform ( InputIterator first1, // 源容器的起始地址
InputIterator last1, // 源容器的终止地址
OutputIterator result, // 目标容器的起始地址
UnaryOperator op ); // 函数指针
// typedef 目标容器元素类型 (*UnaryOperator)(源容器元素类型);
一元操作:op为要进行操作的一元函数对象或sturct、class。
(2)二元操作
二元操作的形式有5个实参,将两个源序列中的元素合并,并将结果写入目标区。
对于二元操作,使用[first1, last1]范围内的每个元素作为第一个参数调用binary_op,并以first2开头的范围内的每个元素作为第二个参数调用binary_op,每次调用返回的值都存储在以result开头的范围内。给定的binary_op将被连续调用last1-first1+1次。binary_op可以是函数指针或函数对象或lambda表达式。
template < class InputIterator1, class InputIterator2,
class OutputIterator, class BinaryOperator >
OutputIterator transform ( InputIterator1 first1, // 源容器1的起始地址
InputIterator1 last1, // 源容器1的终止地址
InputIterator2 first2, // 源容器2的起始地址,元素个数与1相同
OutputIterator result, // 目标容器的起始地址,元素个数与1相同
BinaryOperator binary_op ); // 函数指针
// typedef 目标容器元素类型 (*BinaryOperator)(源容器1元素类型,源容器2元素类型);
二元操作:op为要进行操作的二元函数对象或sturct、class
1.2 注意事项
(1)一元操作:源容器和目标容器可以是一个,这样和for_each() 算法一样,可以用来改动“序列内”的元素。
(2)二元操作:
- 用于将两序列元素结合。
- 调用者必须保证第二源区有足够的空间,至少拥有和第一区间相同的大小。
- 必须确保目标区间有足够的空间。
1.3 用于大小写转换
#include <iostream>
#include <string>
#include <cctype>
#include <algorithm>
using namespace std;
int main()
{
string s = "Hello World";
cout << s << endl;
transform(s.begin(),s.end(),s.begin(),::toupper);//<span style="font-family:'Times New Roman';">::toupper使用定义在全局空间里的</span>to<span style="font-family:'Times New Roman';">upp<span style="font-family:'Times New Roman';">er</span></span>
cout << s << endl;
transform(s.begin(),s.end(),s.begin(),::tolower);//<font face="'Times New Roman'">::t<span style="font-family:'Times New Roman';">olo<span style="font-family:'Times New Roman';">wer<span style="font-family:'Times New Roman';">使用</span></span></span></font><span style="font-family:'Times New Roman';">定义在全局空间里的</span>to<span style="font-family:'Times New Roman';">lower</span>
cout << s << endl;
return 0;
}
toupper和tolower在C++中定义分别在std和cctype中,std中toupper的原型为一个二元函数。
二、应用
用 transform ,以 toupper 函数原位转换字符串为大写,然后转换每个字符为其序数值:
#include <algorithm>
#include <cctype>
#include <iostream>
#include <string>
#include <vector>
int main()
{
std::string s("hello");
std::transform(s.begin(), s.end(), s.begin(),[](unsigned char c) -> unsigned char { return std::toupper(c); });
std::vector<std::size_t> ordinals;
std::transform(s.begin(), s.end(), std::back_inserter(ordinals),
[](unsigned char c) -> std::size_t { return c; });
std::cout << s << ':';
for (auto ord : ordinals) {
std::cout << ' ' << ord;
}
}
输出:
HELLO: 72 69 76 76 79
三、解决G++编译没有匹配函数的问题
3.1 问题描述
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string word="hello";
transform(word.begin(), word.end(), word.begin(), toupper);//转换成大写
cout<<word<<endl;
return 0;
}
在linux gcc编译时,出现 编译没有匹配函数。这里出现错误的原因是Linux将toupper实现为一个宏而不是函数。
windows x86 编译连接成功。
在linux 平台上,使用构建cmake工程编译。
CMakeLists.txt 内容如下:
# 全局变量:CMAKE_SOURCE_DIR CMake的起始目录,即源码的根目录
# 全局变量:PROJECT_NAME 工程的名称
# 全局变量:PROJECT_SOURCE_DIR 工程的源码根目录的完整路径
# 全局变量:构建输出目录。默认的,对于内部构建,此变量的值等于CMAKE_SOURCE_DIR;否则等于构建树的根目录
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/bin) # ${}语法用于引用变量
# 全局变量:可执行文件的输出路径
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR})
# 全局变量:库文件的输出路径
set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR})
# 设置头文件位置
include_directories("${PROJECT_SOURCE_DIR}")
# 设置C++标志位
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# 设置源文件集合
set(SOURCE_FILES main.cpp)
# 添加需要构建的可执行文件,第二个以及后续参数是用于构建此文件的源码文件
add_executable(intellij_taste ${SOURCE_FILES})
构建目录如下:
进入build目录下执行cmake ../
执行make,编译错误如下所示:
3.2 问题定位
std中toupper的原型为一个二元函数,所以使用std::toupper会报错。
3.3 解决方案
可以使用以下方法解决:
#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
#include <cctype>
using namespace std;
int main()
{
string src = "Hello World!";
string dst;
transform(src.begin(), src.end(), back_inserter(dst), ::toupper);
cout << dst << endl;
transform(src.begin(), src.end(), dst.begin(), ::tolower);
cout << dst << endl;
return 0;
}
编译成功:
输出结果正确:
方法二:将toupper 使用函数指针
transform(word.begin(), word.end(), word.begin(), (int (*)(int))toupper);//转换成大写
方法三:
去掉
using namespace std;
问题原因是到底使用了 std库中的 toupper,还是cctype 库中的toupper
参考文献:
【1】C++ transform:https://www.cnblogs.com/balingybj/p/4678880.html
【2】C++/C++11中std::transform的使用:https://blog.csdn.net/fengbingchun/article/details/63252470/
【3】transform函数转换大小写:https://blog.csdn.net/jasonLee_lijiaqi/article/details/78448409
【4】C++/C++11中std::transform的使用:https://blog.csdn.net/lihaidong1991/article/details/96098070
【5】C++ string大小写转换以及transform,tolower,toupper,用法:https://blog.csdn.net/qq_31186409/article/details/50545682
【6】C++11 标准库 第二版
【7】C++ STL中string大小写转换 transform算法中toupper的使用注意事项:https://blog.csdn.net/qq_31186409/article/details/50545682