C++泛型
1.1 模板函数编程
#include <iostream>
template< unsigned int x, unsigned int y >
struct GreatestCommonDivisor {
static const unsigned int result = GreatestCommonDivisor< y, x % y >::result;
};
template< unsigned int x >
struct GreatestCommonDivisor< x, 0 > {
static const unsigned int result = x;
};
constexpr unsigned int greatestCommonDivisor(const unsigned int x, const unsigned y) noexcept {
return y == 0 ? x : greatestCommonDivisor(y, x % y);
}
int main() {
std::cout << GreatestCommonDivisor<40u, 10u>::result << std::endl; // 10
std::cout << GreatestCommonDivisor<366u, 60u>::result << std::endl; // 6
std::cout << greatestCommonDivisor(366u, 60u) << std::endl; // 6
return 0;
}
1.2 仿函数
#include <iostream>
#include <algorithm>
#include <vector>
#include <type_traits>
class IncreasingNumberGenerator { // 生成器类
public:
int operator()() noexcept { return number++; }
private:
int number { 0 };
};
template <typename INTTYPE>
class IsAnOddNumber { // 一元仿函数类
public:
static_assert(std::is_integral<INTTYPE>::value,
"IsAnOddNumber requires an integer type for its parameter!");
constexpr bool operator()(const int value) const noexcept { return (value % 2) != 0; }
};
int main() {
IncreasingNumberGenerator numberGenerator; // 生成器numberGenerator
std::cout << numberGenerator() << std::endl; // 0
std::cout << numberGenerator() << std::endl; // 1
std::vector<int> numbers(10u);
std::generate(std::begin(numbers), std::end(numbers), IncreasingNumberGenerator()); // 0123456789
// erase-remove构造,remove_if接受一元谓词并将不满足谓词的元素移动到容器头部得到02468?????
numbers.erase(std::remove_if(std::begin(numbers), std::end(numbers), IsAnOddNumber<int>()), std::end(numbers));
std::for_each(std::begin(numbers), std::end(numbers), [](const auto& num) { // 02468
std::cout << num;
});
return 0;
}
1.3 Lambda表达式
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
int main() {
std::vector<std::string> quote { "one", "step", "for", "a", "man,", "one", "giant", "leap", "for", "mankind.", };
std::vector<std::string> result;
// back_inserter 返回使用push_back的迭代器
std::transform(std::begin(quote), std::end(quote), std::back_inserter(result), [](const std::string& word) {
return "<" + word + ">";
});
std::for_each(std::begin(result), std::end(result), [](const auto& word) {
std::cout << word << " ";
});
// <one> <step> <for> <a> <man,> <one> <giant> <leap> <for> <mankind.>
return 0;
}
1.4 高阶函数
函数式编程语言中三个重要的高阶函数Map, Filter & Fold(Reduce)。
C++中Map对应std::transform,Filter对应std::remove_if,Fold对应std::accumulate。
#include <iostream>
#include <functional>
#include <vector>
template <typename CONTAINERTYPE, typename UNARYFUNCTIONTYPE>
void myForEach(const CONTAINERTYPE& container, UNARYFUNCTIONTYPE unaryFunction) {
for (const auto& element : container) {
unaryFunction(element);
}
}
template <typename CONTAINERTYPE, typename UNARYOPERATIONTYPE>
void myTransform(CONTAINERTYPE& container, UNARYOPERATIONTYPE unaryOperator) {
for (auto& element : container) {
element = unaryOperator(element);
}
}
template <typename NUMBERTYPE>
class ToSquare {
public:
NUMBERTYPE operator()(const NUMBERTYPE& number) const noexcept {
return number * number;
}
};
template <typename TYPE>
void printOnStdOut(const TYPE& thing) {
std::cout << thing << ", ";
}
int main() {
std::vector<int> numbers { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, };
myTransform(numbers, ToSquare<int>());
std::function<void(int)> printNumberOnStdOut = printOnStdOut<int>;
myForEach(numbers, printNumberOnStdOut); // 1, 4, 9, 16, 25, 36, 49, 64, 81, 100,
return 0;
}
// 删掉字符串容器中的回文字符串
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
class IsPalindrome {
public:
bool operator()(const std::string& word) const {
const auto middleOfWord = std::begin(word) + word.size() / 2;
return std::equal(std::begin(word), middleOfWord, std::rbegin(word));
}
};
int main() {
std::vector<std::string> someWords { "dad", "hello", "radar", "vector", "deleveled", "foo", "bar", "racecar", "ROTOR", "", "C++", "aibohphobia", };
someWords.erase(std::remove_if(std::begin(someWords), std::end(someWords), IsPalindrome()), std::end(someWords));
std::for_each(std::begin(someWords), std::end(someWords), [](const auto& word) { // hello,vector,foo,bar,C++,
std::cout << word << ",";
});
return 0;
}
1.5 Fold操作
#include <iostream>
#include <numeric>
#include <vector>
int main() {
std::vector<int> numbers { 12, 45, -102, 33, 78, -8, 100, 2017, -110, };
const int sum = std::accumulate(std::begin(numbers), std::end(numbers), 0); // ((A+B)+C)+D
std::cout << sum << std::endl; // 求和 2065
// 如果用rbegin和rend就是A+(B+(C+D)), 第三个参数0是initValue
const int maxValue = std::accumulate(std::begin(numbers), std::end(numbers), 0, [](const int value1, const int value2) {
return value1 > value2 ? value1 : value2; // ((A, B), C), D
});
std::cout << maxValue << std::endl; // 求最大值 2017
return 0;
}
#include <iostream>
template <typename... PACK>
int substractFold(int minuend, PACK... subtrahends) { // A - B - C ...
return (minuend - ... - subtrahends);
}
template <typename... TYPE>
bool containsEvenValue(const TYPE&... argument) { // 是否包含偶数
return ((argument % 2 == 0) || ...);
}
int main() {
const int result = substractFold(1000, 10, 1, 100);
std::cout << result << std::endl; // 889
const bool result1 = containsEvenValue(10, 7, 11);
const bool result2 = containsEvenValue(13, 7, 11);
std::cout << result1 << std::endl; // 1
std::cout << result2 << std::endl; // 0
return 0;
}
1.6 其他泛型用法
string sum = accumulate(v.begin(), v.end(), string("")); // 拼接字符串
equal(roster1.cbegin(), roster1.cend(), roster2.cbegin()); // roster的size相等
fill(vec.begin(), vec.begin() + vec.size()/2, 0); // fill的第二个参数是截至位置的迭代器
fill_n(vec.begin(), vec.size()/2, 0); // fill_n的第二个参数是元素个数
fill_n(back_insert(vec), 10, 0); // 在vec的尾部添加10个10
sort(words.begin(), words.end()); // 使words中元素唯一,unique与remove_if用法类似
words.erase(unique(words.begin(), words.end()), words.end());
auto wc = find_if(words.begin(), words.end(), [sz](const string& word) {
return word.size() > sz;
}); // 返回第一个满足谓词的迭代器
1.7 标准库方法

string s1;//初始化字符串,空字符串
string s2 = s1; //拷贝初始化,深拷贝字符串
string s3 = "I am b"; //直接初始化,s3存了字符串
string s4(10, 'a'); //s4存的字符串是aaaaaaaaaa
string s5(s4); //拷贝初始化,深拷贝字符串
string s6("I am d"); //直接初始化
string s7 = string(6, 'c'); //拷贝初始化,cccccc
str.append(args) // 在尾部添加一个字符或一个字符
str.replace(pos, args) // 在尾部添加一个字符或一个字符 ,它的重载函数很多,共16个。
str.substr(_pos, n) //该函数可以获得原字符串中的部分字符, 从pos开始的n个字符,当_pos超过范围时,会抛出out_of_range的异常yl
str.find(args) //查找args 第一次出现的位置
str.rfind(args) //查找args最后一次出现的位置
str.find_first_of(args) //搜索的是字符, 第一个是args里的字符的位置y
str.find_last_of(args) // 搜索的是字符, 最后一个是args里的字符的位置l
str.find_first_not_of() // 搜索的是字符,第一个不是args里的字符的位置
str.find_last_not_of() // 搜索的是字符, 最后一个不是args里的字符的位置


- 属性操作
v1.size() //v1内已经存放的元素的数目
v1.capacity() // v1现有的在存储容量(不再一次进行扩张内存空间的前提下)
v1.empty() // 判断v1是否为空
v1.max_size() // 返回vector可以存放的最大元素个数,一般这个数很大,因为vector可以不断调整容量大小。
v1.shrink_to_fit() // 该函数会把v1的capacity()的大小压缩到size()大小,即释放多余的内存空间。
- 访问操作
v1[n] // 通过下标进行访问vector中的元素的引用 (下标一定要存在 ,否则未定义,软件直接崩了)
v1.at(n) // 与上面类似,返回下标为n的元素的引用,不同的是,如果下标不存在,它会抛出out_of_range的异常。它是安全的,建议使用它。
v1.front() // 返回vector中头部的元素的引用(使用时,一定要进行非空判断)
v1.back() // 返回vector中尾部的元素 引用(使用时,一定要进行非空判断)
- 添加操作
v1.push_back(a) //在迭代器的尾部添加一个元素
v1.push_front(a) // vector不支持这个操作
v1.insert(iter, a) // 将元素a 插入到迭代器指定的位置的前面,返回新插入元素的迭代器(在c++11标准之前的版本,返回void)
v1.insert(iter, iter1, iter2) //把迭代器[iterator1, iterator2]对应的元素插入到迭代器iterator之前的位置,返回新插入的第一个元素的迭代器(在c++11标准之前的版本, 返回空)。
在c++11标准中,引入了emplac_front()、 emplace()、emplace_back(), 它们分别与push_front()、insert()、 push_back()相对应,用法与完成的动作作完全相同,但是实现不一样。 push_front()、insert()各push_back()是对元素使用copy操作来完成的,而emplac_front()、 emplace()和emplace_back()是对元素使用构造来完成的,后者的效率更高,避免了不必要的操作。因此,在以后更后推荐使用它们。
- 删除操作
v1.erase(iterator) // 删除人人迭代器指定的元素,返回被删除元素之后的元素的迭代器。(效率很低,最好别用)
v1.pop_front() //vector不支持这个操作
v1.pop_back() //删除vector尾部的元素 , 返回void类型 (使用前,一定要记得非空判断)
v1.clear() //清空所有元素
- 替换操作
v1.assign({初始化列表}) // 它相当于赋值操作,
v1.assign(n, T) // 此操作与初始化时的操作类似,用个n T类型的元素对v1进行赋值
v1.assign(iter1, iter2) // 使用迭代器[iter1, iter2]区间内的元素进行赋值(该迭代器别指向自身就可以),另外,只要迭代器指的元素类型相同即可(存放元素的容器不同,例如:可以用list容器内的值对vector容器进行assign操作,而用 "=" 绝对做不到的。
v1.swap(v2) // 交换v1与v2中的元素。
swap操作速度很快,因为它是通过改变v1与v2两个容器内的数据结构(可能是类似指针之类的与v1和v2的绑定)完成的,不会对容器内的每一个元素进行交换。 这样做,不仅速度快,并且指向原容器的迭代器、引用以及指针等仍然有效,因为原始的数据没有变。在c++ primer 中建议大家使用非成员版本的swap()函数,它在范型编程中很重要。
1.8 其他容器
deque
deque模版类 double-ended queue(双端队列) ,支持随机访问,从deque对象的开始位置插入和删除元素的时间是固定的,而不像vector是线性时间。 所以多数操作是发生在序列的起始和结尾处,就应该考虑deque。为实现在deque两端执行插入和删除操作的时间为固定的这一目的,deque对象的设计比vector对象更为复杂。 因此,尽管两者都提供对元素的随机访问和在序列中部执行线性时间的插入和删除操作,但是vector容器执行这些操作时速度还要快些。
list
list(双向链表),除了第一个和最后一个元素外,每个元素都与前后的元素相连接,这意味着可以双向遍历链表,list和vector之间关键区别在于,list在链表中任一位置进行插入和删除的时间都是固定的(vector模版提供了除结尾处外的线性时间的插入和删除,在结尾处,它提供了固定时间的插入和删除)。因此,vector强调的是通过随机访问快速访问,而list强调的是元素的快速插入和删除。list也可以反转容器,list不支持数组表示法和随机访问。因为它是用双向链表实现的,所以,它的一大特性就是它的迭代器永远不会变为无效(除非这段空间不存在了),即无论增加、删除操作,都不会破坏迭代器。list是一个双向链表,而单链表对应的容器则是foward_list。
forward_list
forward_list实现了单链表,在这种链表中,每个节点都只链接到下一个节点,而没有链接到前一个节点。因此forward_list只需要正向迭代器,而不需要双向迭代器。因此,不同于vector和list,forward_list是不可反转的容器。相比list,forward_list更简单,更紧凑,但功能也更少。
queue
queue模版的限制比deque更多,它不仅不允许随机访问队列元素,甚至不允许遍历队列。它把使用限制在定义队列的基本操作上,可以将元素添加到队尾,从队首删除元素,查看队首和队尾的值,检查元素数目和测试队列是否为空。
priority_queue
priority_queue和queue的区别在于,最大的元素被移到队首。内部区别在于,默认的底层类是vector。可以修改用于确定哪个元素放到队首的比较方式。方法是提供一个构造函数。
stack
stack提供了典型的栈接口,stack模版的限制比vector更多。它不仅不允许随机访问栈元素,甚至不允许遍历栈,它把使用限制在定义栈的基本操作上,即可以将压入推到栈顶,从栈顶弹出元素,查看栈顶的值,检查元素数目和测试栈是否为空.于queue想死,如果要使用栈中的值,必须首先使用top()来检索这个值,然后使用pop将它中栈中删除。
关联容器
set
其值类型与键相同,键是唯一的。这意味着集合中不会有多个相同的键。set跟vector差不多,它跟vector的唯一区别就是,set里面的元素是有序的且唯一的,只要你往set里添加元素,它就会自动排序,而且,如果你添加的元素set里面本来就存在,那么这次添加操作就不执行。
multiset
可以有多个相同的键。
map
map运用了哈希表地址映射的思想,也就是key-value的思想,来实现的,底层和set一样都是用红黑树实现。内部元素按照key哈希排序。首先给出map最好用也最最常用的用法例子,就是用字符串作为key去查询操作对应的value。注意:在查找一个不存在的key的时候,map会自动生成这个key,这里涉及到如何安全和挑选符合自己业务逻辑的关联容器格外重要
multimap
multimap也是可以反转的,经过排序的关联容器,但键和值的类型不同,且同一个键可能与多个值相关联。
四种无关联容器
无序关联容器是对容器概念的另一种改进。与关联容器一样,无序关联容器也将键与值关联起来,并使用键来查找值,但底层的差别在于,关联容器是基于树结构的,而无序关联容器是基于数据结构哈希表的
unordered_set
unordered_multiset
unordered_map
unordered_mutimap
编写代码时,总要把维护你的代码的人当作是知道你住在哪里的暴力精神病患者。
std::all_of
// all_of example
#include <iostream> // std::cout
#include <algorithm> // std::all_of
#include <array> // std::array
int main () {
std::array<int,8> foo = {3,5,7,11,13,17,19,23};
if ( std::all_of(foo.begin(), foo.end(), [](int i){return i%2;}) )
std::cout << "All the elements are odd numbers.\n";
return 0;
}
std::adjacent_find
// adjacent_find example
#include <iostream> // std::cout
#include <algorithm> // std::adjacent_find
#include <vector> // std::vector
bool myfunction (int i, int j) {
return (i==j);
}
int main () {
int myints[] = {5,20,5,30,30,20,10,10,20};
std::vector<int> myvector (myints,myints+8);
std::vector<int>::iterator it;
// using default comparison:
it = std::adjacent_find (myvector.begin(), myvector.end());
if (it!=myvector.end())
std::cout << "the first pair of repeated elements are: " << *it << '\n';
//using predicate comparison:
it = std::adjacent_find (++it, myvector.end(), myfunction);
if (it!=myvector.end())
std::cout << "the second pair of repeated elements are: " << *it << '\n';
return 0;
}
std::any_of
// any_of example
#include <iostream> // std::cout
#include <algorithm> // std::any_of
#include <array> // std::array
int main () {
std::array<int,7> foo = {0,1,-1,3,-3,5,-5};
if ( std::any_of(foo.begin(), foo.end(), [](int i){return i<0;}) )
std::cout << "There are negative elements in the range.\n";
return 0;
}
std::equal
// equal algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::equal
#include <vector> // std::vector
bool mypredicate (int i, int j) {
return (i==j);
}
int main () {
int myints[] = {20,40,60,80,100}; // myints: 20 40 60 80 100
std::vector<int>myvector (myints,myints+5); // myvector: 20 40 60 80 100
// using default comparison:
if ( std::equal (myvector.begin(), myvector.end(), myints) )
std::cout << "The contents of both sequences are equal.\n";
else
std::cout << "The contents of both sequences differ.\n";
myvector[3]=81; // myvector: 20 40 60 81 100
// using predicate comparison:
if ( std::equal (myvector.begin(), myvector.end(), myints, mypredicate) )
std::cout << "The contents of both sequences are equal.\n";
else
std::cout << "The contents of both sequences differ.\n";
return 0;
}
std::generate
// generate algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::generate
#include <vector> // std::vector
#include <ctime> // std::time
#include <cstdlib> // std::rand, std::srand
// function generator:
int RandomNumber () { return (std::rand()%100); }
// class generator:
struct c_unique {
int current;
c_unique() {current=0;}
int operator()() {return ++current;}
} UniqueNumber;
int main () {
std::srand ( unsigned ( std::time(0) ) );
std::vector<int> myvector (8);
std::generate (myvector.begin(), myvector.end(), RandomNumber);
std::cout << "myvector contains:";
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
std::generate (myvector.begin(), myvector.end(), UniqueNumber);
std::cout << "myvector contains:";
for (std::vector<int>::iterator it=myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
std::includes
// includes algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::includes, std::sort
bool myfunction (int i, int j) { return i<j; }
int main () {
int container[] = {5,10,15,20,25,30,35,40,45,50};
int continent[] = {40,30,20,10};
std::sort (container,container+10);
std::sort (continent,continent+4);
// using default comparison:
if ( std::includes(container,container+10,continent,continent+4) )
std::cout << "container includes continent!\n";
// using myfunction as comp:
if ( std::includes(container,container+10,continent,continent+4, myfunction) )
std::cout << "container includes continent!\n";
return 0;
}
std::set_difference
// set_difference example
#include <iostream> // std::cout
#include <algorithm> // std::set_difference, std::sort
#include <vector> // std::vector
int main () {
int first[] = {5,10,15,20,25};
int second[] = {50,40,30,20,10};
std::vector<int> v(10); // 0 0 0 0 0 0 0 0 0 0
std::vector<int>::iterator it;
std::sort (first,first+5); // 5 10 15 20 25
std::sort (second,second+5); // 10 20 30 40 50
it=std::set_difference (first, first+5, second, second+5, v.begin());
// 5 15 25 0 0 0 0 0 0 0
v.resize(it-v.begin()); // 5 15 25
std::cout << "The difference has " << (v.size()) << " elements:\n";
for (it=v.begin(); it!=v.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
std::unique
// unique algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::unique, std::distance
#include <vector> // std::vector
bool myfunction (int i, int j) {
return (i==j);
}
int main () {
int myints[] = {10,20,20,20,30,30,20,20,10}; // 10 20 20 20 30 30 20 20 10
std::vector<int> myvector (myints,myints+9);
// using default comparison:
std::vector<int>::iterator it;
it = std::unique (myvector.begin(), myvector.end()); // 10 20 30 20 10 ? ? ? ?
// ^
myvector.resize( std::distance(myvector.begin(),it) ); // 10 20 30 20 10
// using predicate comparison:
std::unique (myvector.begin(), myvector.end(), myfunction); // (no changes)
// print out content:
std::cout << "myvector contains:";
for (it=myvector.begin(); it!=myvector.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
std::transform
// transform algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::transform
#include <vector> // std::vector
#include <functional> // std::plus
int op_increase (int i) { return ++i; }
int main () {
std::vector<int> foo;
std::vector<int> bar;
// set some values:
for (int i=1; i<6; i++)
foo.push_back (i*10); // foo: 10 20 30 40 50
bar.resize(foo.size()); // allocate space
std::transform (foo.begin(), foo.end(), bar.begin(), op_increase);
// bar: 11 21 31 41 51
// std::plus adds together its two arguments:
std::transform (foo.begin(), foo.end(), bar.begin(), foo.begin(), std::plus<int>());
// foo: 21 41 61 81 101
std::cout << "foo contains:";
for (std::vector<int>::iterator it=foo.begin(); it!=foo.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
std::remove
std::is_permutation
emplate <class InputIterator1, class InputIterator2>
bool is_permutation (InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2)
{
std::tie (first1,first2) = std::mismatch (first1,last1,first2);
if (first1==last1) return true;
InputIterator2 last2 = first2; std::advance (last2,std::distance(first1,last1));
for (InputIterator1 it1=first1; it1!=last1; ++it1) {
if (std::find(first1,it1,*it1)==it1) {
auto n = std::count (first2,last2,*it1);
if (n==0 || std::count (it1,last1,*it1)!=n) return false;
}
}
return true;
}
std::mismatch
// mismatch algorithm example
#include <iostream> // std::cout
#include <algorithm> // std::mismatch
#include <vector> // std::vector
#include <utility> // std::pair
bool mypredicate (int i, int j) {
return (i==j);
}
int main () {
std::vector<int> myvector;
for (int i=1; i<6; i++) myvector.push_back (i*10); // myvector: 10 20 30 40 50
int myints[] = {10,20,80,320,1024}; // myints: 10 20 80 320 1024
std::pair<std::vector<int>::iterator,int*> mypair;
// using default comparison:
mypair = std::mismatch (myvector.begin(), myvector.end(), myints);
std::cout << "First mismatching elements: " << *mypair.first;
std::cout << " and " << *mypair.second << '\n';
++mypair.first; ++mypair.second;
// using predicate comparison:
mypair = std::mismatch (mypair.first, myvector.end(), mypair.second, mypredicate);
std::cout << "Second mismatching elements: " << *mypair.first;
std::cout << " and " << *mypair.second << '\n';
return 0;
}
std::find_if
// find_if example
#include <iostream> // std::cout
#include <algorithm> // std::find_if
#include <vector> // std::vector
bool IsOdd (int i) {
return ((i%2)==1);
}
int main () {
std::vector<int> myvector;
myvector.push_back(10);
myvector.push_back(25);
myvector.push_back(40);
myvector.push_back(55);
std::vector<int>::iterator it = std::find_if (myvector.begin(), myvector.end(), IsOdd);
std::cout << "The first odd value is " << *it << '\n';
return 0;
}
std::count_if
// count_if example
#include <iostream> // std::cout
#include <algorithm> // std::count_if
#include <vector> // std::vector
bool IsOdd (int i) { return ((i%2)==1); }
int main () {
std::vector<int> myvector;
for (int i=1; i<10; i++) myvector.push_back(i); // myvector: 1 2 3 4 5 6 7 8 9
int mycount = count_if (myvector.begin(), myvector.end(), IsOdd);
std::cout << "myvector contains " << mycount << " odd values.\n";
return 0;
}
std::copy_if
// copy_if example
#include <iostream> // std::cout
#include <algorithm> // std::copy_if, std::distance
#include <vector> // std::vector
int main () {
std::vector<int> foo = {25,15,5,-5,-15};
std::vector<int> bar (foo.size());
// copy only positive numbers:
auto it = std::copy_if (foo.begin(), foo.end(), bar.begin(), [](int i){return !(i<0);} );
bar.resize(std::distance(bar.begin(),it)); // shrink container to new size
std::cout << "bar contains:";
for (int& x: bar) std::cout << ' ' << x;
std::cout << '\n';
return 0;
}
std::not1
// not1 example
#include <iostream> // std::cout
#include <functional> // std::not1
#include <algorithm> // std::count_if
struct IsOdd {
bool operator() (const int& x) const {return x%2==1;}
typedef int argument_type;
};
int main () {
int values[] = {1,2,3,4,5};
int cx = std::count_if (values, values+5, std::not1(IsOdd()));
std::cout << "There are " << cx << " elements with even values.\n";
return 0;
}
std::not2
// not2 example
#include <iostream> // std::cout
#include <functional> // std::not2, std::equal_to
#include <algorithm> // std::mismatch
#include <utility> // std::pair
int main () {
int foo[] = {10,20,30,40,50};
int bar[] = {0,15,30,45,60};
std::pair<int*,int*> firstmatch,firstmismatch;
firstmismatch = std::mismatch (foo, foo+5, bar, std::equal_to<int>());
firstmatch = std::mismatch (foo, foo+5, bar, std::not2(std::equal_to<int>()));
std::cout << "First mismatch in bar is " << *firstmismatch.second << '\n';
std::cout << "First match in bar is " << *firstmatch.second << '\n';
return 0;
}
std::any_cast
#include <string>
#include <iostream>
#include <any>
#include <utility>
int main()
{
// simple example
auto a = std::any(12);
std::cout << std::any_cast<int>(a) << '\n';
try {
std::cout << std::any_cast<std::string>(a) << '\n';
}
catch(const std::bad_any_cast& e) {
std::cout << e.what() << '\n';
}
// pointer example
if (int* i = std::any_cast<int>(&a)) {
std::cout << "a is int: " << *i << '\n';
} else if (std::string* s = std::any_cast<std::string>(&a)) {
std::cout << "a is std::string: " << *s << '\n';
} else {
std::cout << "a is another type or unset\n";
}
// advanced example
a = std::string("hello");
auto& ra = std::any_cast<std::string&>(a); //< reference
ra[1] = 'o';
std::cout << "a: "
<< std::any_cast<const std::string&>(a) << '\n'; //< const reference
auto b = std::any_cast<std::string&&>(std::move(a)); //< rvalue reference
// Note: 'b' is a move-constructed std::string,
// 'a' is left in valid but unspecified state
std::cout << "a: " << *std::any_cast<std::string>(&a) //< pointer
<< "b: " << b << '\n';
}
std::optional
#include <string>
#include <functional>
#include <iostream>
#include <optional>
// optional can be used as the return type of a factory that may fail
std::optional<std::string> create(bool b) {
if (b)
return "Godzilla";
return {};
}
// std::nullopt can be used to create any (empty) std::optional
auto create2(bool b) {
return b ? std::optional<std::string>{"Godzilla"} : std::nullopt;
}
// std::reference_wrapper may be used to return a reference
auto create_ref(bool b) {
static std::string value = "Godzilla";
return b ? std::optional<std::reference_wrapper<std::string>>{value}
: std::nullopt;
}
int main()
{
std::cout << "create(false) returned "
<< create(false).value_or("empty") << '\n';
// optional-returning factory functions are usable as conditions of while and if
if (auto str = create2(true)) {
std::cout << "create2(true) returned " << *str << '\n';
}
if (auto str = create_ref(true)) {
// using get() to access the reference_wrapper's value
std::cout << "create_ref(true) returned " << str->get() << '\n';
str->get() = "Mothra";
std::cout << "modifying it changed it to " << str->get() << '\n';
}
}
tuple
// tuple example
#include <iostream> // std::cout
#include <tuple> // std::tuple, std::get, std::tie, std::ignore
int main ()
{
std::tuple<int,char> foo (10,'x');
auto bar = std::make_tuple ("test", 3.1, 14, 'y');
std::get<2>(bar) = 100; // access element
int myint; char mychar;
std::tie (myint, mychar) = foo; // unpack elements
std::tie (std::ignore, std::ignore, myint, mychar) = bar; // unpack (with ignore)
mychar = std::get<3>(bar);
std::get<0>(foo) = std::get<2>(bar);
std::get<1>(foo) = mychar;
std::cout << "foo contains: ";
std::cout << std::get<0>(foo) << ' ';
std::cout << std::get<1>(foo) << '\n';
return 0;
}
// make_tuple example
#include <iostream>
#include <tuple>
#include <functional>
int main()
{
auto first = std::make_tuple (10,'a'); // tuple < int, char >
const int a = 0; int b[3]; // decayed types:
auto second = std::make_tuple (a,b); // tuple < int, int* >
auto third = std::make_tuple (std::ref(a),"abc"); // tuple < const int&, const char* >
std::cout << "third contains: " << std::get<0>(third);
std::cout << " and " << std::get<1>(third);
std::cout << std::endl;
return 0;
}
// forward_as_tuple example
#include <iostream> // std::cout
#include <tuple> // std::tuple, std::get, std::forward_as_tuple
#include <string> // std::string
void print_pack (std::tuple<std::string&&,int&&> pack) {
std::cout << std::get<0>(pack) << ", " << std::get<1>(pack) << '\n';
}
int main() {
std::string str ("John");
print_pack (std::forward_as_tuple(str+" Smith",25));
print_pack (std::forward_as_tuple(str+" Daniels",22));
return 0;
}
// John Smith, 25
// John Daniels, 22
#include <variant>
#include <string>
#include <iostream>
int main()
{
std::variant<int, float> v{12}, w;
std::cout << std::get<int>(v) << '\n';
w = std::get<int>(v);
w = std::get<0>(v); // same effect as the previous line
// std::get<double>(v); // error: no double in [int, float]
// std::get<3>(v); // error: valid index values are 0 and 1
try
{
w = 42.0f;
std::cout << std::get<float>(w) << '\n'; // ok, prints 42
w = 42;
std::cout << std::get<float>(w) << '\n'; // throws
}
catch (std::bad_variant_access const& ex)
{
std::cout << ex.what() << ": w contained int, not float\n";
}
}
#include <iostream>
#include <tuple>
#include <utility>
int add(int first, int second) { return first + second; }
template<typename T>
T add_generic(T first, T second) { return first + second; }
auto add_lambda = [](auto first, auto second) { return first + second; };
template<typename... Ts>
std::ostream& operator<<(std::ostream& os, std::tuple<Ts...> const& theTuple)
{
std::apply
(
[&os](Ts const&... tupleArgs)
{
os << '[';
std::size_t n{0};
((os << tupleArgs << (++n != sizeof...(Ts) ? ", " : "")), ...);
os << ']';
}, theTuple
);
return os;
}
int main()
{
// OK
std::cout << std::apply(add, std::pair(1, 2)) << '\n';
// Error: can't deduce the function type
// std::cout << std::apply(add_generic, std::make_pair(2.0f, 3.0f)) << '\n';
// OK
std::cout << std::apply(add_lambda, std::pair(2.0f, 3.0f)) << '\n';
// advanced example
std::tuple myTuple(25, "Hello", 9.31f, 'c');
std::cout << myTuple << '\n';
}
// 3
// 5
// [25, Hello, 9.31, c]
C++17使用std::apply和fold expression对std::tuple进行遍历
std::apply函数
先来看这个std::apply函数,这个函数定义在tuple头文件中,函数签名如下:
template <class F, class Tuple>
constexpr decltype(auto) apply(F&& f, Tuple&& t);
该函数接受两个参数,第一个是一个函数对象,第二个是一个Tuple对象
来看一个最简单的示例:
#include <tuple>
#include <iostream>
int main() {
// 两个元素相加
std::cout << std::apply([](auto x, auto y) { return x + y; },
std::make_tuple(1, 2.0)) << '\n';
}
输出结果是3
这个例子中第一个参数使用Lambda匿名函数将tuple中的两个元素相加,第二个使用std::make_tuple函数构造一个只含有两个元素的tuple
fold expression
这个特性是C++ 17中我觉得很有用的一个新特性,使用规则有四条
可能看这个规则有些抽象,我们来看一些具体的例子:
#include <tuple>
#include <iostream>
int main() {
// 多个元素相加,使用parameter pack
std::cout << std::apply([](auto&& ... args) { return (args + ...); },
std::make_tuple(1, 2.f, 3.0)) << '\n';
// 遍历tuple并输出,注意逗号操作符的使用
std::apply([](auto&&... args) { ((std::cout << args << '\n'), ...); },
std::make_tuple(1, 2.f, 3.0));
}
输出如下:
6
1
2
3
第6行中,std::apply函数的第一个参数是一个Lambda匿名函数,函数的参数是一个可变参数args,函数体中只有一条语句args + ...,这个情况就是上面的第一种情况:这里的E EE就是args,o p opo**p就是+,所以展开来就是a r g s 1 + a r g s 2 + a r g s 3 args_1 + args_2 + args_3arg**s1+arg**s2+arg**s3(因为参数的个数是3)。
第9行中,Lambda匿名函数的函数体是((std::cout << args << '\n'), ...)这是一个逗号操作符,也属于上面四种情况中的第一种:这里的E EE就是std::cout << args << '\n'),o p opo**p就是,,所以这一行就打印输出了tuple的每一个元素。如果在C++17之前想要遍历tuple就比较麻烦,需要很多额外的操作。