一,函数对象
函数对象相比于函数的好处,是:
1)类或结构体中可以有自己的成员变量;
2)对于标准模版库STL算法中,比如配合std::for_each/find_if/sort/partition/remove_if等,使用方便;
#include <iostream> #include <string> using namespace std; template <typename T> int funcAdd(T t1, T t2) { return t1 + t2; } template <typename T> struct classAdd { // 函数对象必须是实现operator(),参数和返回值可以自定 // struct和class一样,如果是class需要声明public int operator() (const T t1, const T t2) const { return t1 + t2; } }; int main() { int funcSum = funcAdd(1, 2); string result = "func result:" + to_string(funcSum); cout << result.c_str() << endl; auto obj = classAdd<int>(); // 函数对象 int classSum = obj(1, 2); // 也可以写成:classAdd<int>()(a1, a2) result = "class result:" + to_string(classSum); cout << result.c_str() << endl; system("pause"); return 0; }
二,lambda表达式
lambda表达式被视为包含公有operator()的匿名结构体(或类)。从这种意义上说,lambda表达式属于函数对象。所以lambda表达式也叫lambda函数。
auto lambda = [](const int a, const int b) {return a + b; }; int lambdaSum = lambda(1, 2); result = "lambda result:" + to_string(lambdaSum); cout << result.c_str() << endl;
int base = 5; auto lambda = [base](const int a, const int b) {return a + b + base; }; int lambdaSum = lambda(1, 2); // result: 8 result = "lambda result:" + to_string(lambdaSum); cout << result.c_str() << endl;
int base = 5; auto lambda = [base](const int a, const int b) mutable { base = 10; // 没有mutable关键字,这里报错,但其实外部的值还是没有改变 return a + b + base; }; lambda(1, 2); cout << "base1:" << base << endl; // print 5 auto lambda2 = [&base](const int a, const int b) { base = 20; // 可以修改 return a + b + base; }; lambda2(1, 2); cout << "base2:" << base << endl; // print 20
int base = 5; auto lambda = [=](const int a, const int b) mutable -> int // =号声明可以使用外部所有变量 { base = 10; // 但不允许修改 return a + b + base; }; int sum = lambda(1, 2); cout << "base:" << base << endl; // print 5 cout << "sum:" << sum << endl; // print 8
lambda的[] 中,除了具体的变量a或引用&a,可以使用:=,&,this等:
1)=:lambda可以使用,所在函数体内所有可见的局部变量(包括所在类的this),按值传递方式。
2)&:lamdda可以使用,所在函数体内所有可见的局部变量(包括所在类的this),按引用传递方式。
3)this:lamdda可以使用,所在所在类的成员变量;
4)空:没有任何对象参数;
auto lambda = [](const int a, const int b) -> int // ->ReturnType 明确指明返回类型 { return a + b; }; int sum = lambda(1, 2); cout << "sum:" << sum << endl; // print 3
lambda表达式应该尽量简短,跨越多行调用lambda表达式可能无助于提高编程效率,此时应使用易于重用的函数对象。
三,std::bitset,用于处理位和位标志的STL类,不允许调整长度。
#include <iostream> #include <string> #include <bitset> using namespace std; int main() { bitset<5> fiveBits; cout << fiveBits << endl; // print: 00000 bitset<4> fourBits(5);//或fourBits("0101")或fourBits(0b0101) cout << fourBits << endl; // print: 0101 cout << fourBits[2] << endl; // 获取第三位,print: 1 bitset<4> fourBits2 = fourBits << (2); // 左移两位 cout << fourBits2 << endl; // print: 0100 bitset<4> result(fourBits & fourBits2); // 执行& cout << result << endl; // print: 0100 fourBits.set(); // 全部置1 cout << fourBits << endl; // print: 1111 fourBits.set(1, 0); // 第二位置0 cout << fourBits << endl; // print: 1101 fourBits.reset(0); // 第一位置0 cout << fourBits << endl; // print: 1100 fourBits.reset(); // 全部置0 cout << fourBits << endl; // print: 0000 fourBits.flip(); // 取反 cout << fourBits << endl; // print: 1111 cout << fourBits.size() << endl; // 返回位数 cout << fourBits.count() << endl; // 返回为1的数量 system("pause"); return 0; }
四,自定义异常
1,注意throw字符串的捕获;
2,注意自定义异常继承时,声明public的区别;
#include <iostream> #include <string> #include <exception> using namespace std; class MyException : public std::exception // 如果是public继承,则catch (const std::exception& e)可以捕获 { string reason; public: MyException(const char* why) :reason(why) { } virtual const char* what() const throw() { return reason.c_str(); } }; void test1() { throw "exception test..."; // catch (const char* errorMsg)捕获 } void test2() { throw MyException("MyException"); // throw 自定义异常 } int main() { try { //test1(); test2(); } catch (MyException& e) // 捕获自定义异常 { cout << "MyException:" << e.what() << endl; } catch (const std::exception& e) { cout << "std::exception:" << e.what() << endl; } catch (const char* errorMsg) // 捕获throw出来的字符串 { cout << "errorMsg:" << errorMsg << endl; } system("pause"); return 0; }
五,nullptr和NULL
nullptr的出现是为了替代NULL。在某种意义上说,传统C++会把NULL、0视为同一种东西,这取决于编译器如何定义NULL。有些编译器会将NULL定义为((void*)0),而有些则会直接将其定义为0。
C++不允许直接将void*隐式转换到其它类型,但如果NULL被定义为((void*)0),那么当编译:
char *ch = NULL;
时,NULL只好被定义为0。而这依然会产生问题,将导致C++中重载发生混乱。
void foo(char*) void foo(int);
例如对于这两个函数,如果NULL被定义为0,那么foo(NULL)将调用foo(int),从而导致代码违反直观。
为了解决这个问题,c++11引入了nullptr关键字,专门用于区分空指针和0。nullptr的类型为nullptr_t,能够隐式地转换为任何指针或成员指针的类型,也能和他们进行相等或不等的比较。
#include <iostream> #include <string> using namespace std; void foo(int i) { cout << "foo(int)" << endl; } void foo(char* c) { cout << "foo(char*)" << endl; } int main() { foo(1); // print foo(int) foo(NULL); // print foo(int) foo(nullptr); // print foo(char*) system("pause"); return 0; }
六,decltype关键字
decltype是为了解决auto只能对变量进行类型推导的缺陷而出现。decltype根据表达式来推倒类型。
decltype(表达式)
在函数声明中,与auto配合来推导函数返回类型:
#include <iostream> #include <string> using namespace std; template <typename T, typename U> auto add(T t, U u) ->decltype(t+u) // decltype用于智能推导表达式(t+u)的类型 { return t + u; } int main() { auto result = add(1, 5.1); cout << result << endl; auto a1 = 2; auto a2 = 2.5; decltype(a1*a2) r1 = a1 + a2; // decltype用于智能推导表达式(a1*a2)的类型 cout << r1 << endl; // print 3.5 decltype(a1) r2 = a1 + a2; // decltype用于智能推导表达式(a1)的类型 cout << r2 << endl; // print 3.5 system("pause"); return 0; }
在C++14中不用声明使用->decltype来推导函数返回类型。我手头上的C++11也行。
template <typename T, typename U> auto add(T t, U u) { return t + u; }
推导规则:decltype(exp)
1)exp是标识符、类访问表达式,decltype(exp)和exp的类型一致;
2)exp是函数调用,decltype(exp)和返回值的类型一致;
3)其它情况,若exp是一个左值,则decltype(exp)是exp类型的左值引用,否则和exp类型一致。
decltype也经常用在通过变量表达式抽取变量类型上:
vector<int> v; //...... decltype(v)::value_type i = 0; // 只要知道v是一个容器就可以了。
七,外部模版
在传统C++中,模板只有在使用时才会被编译器实例化。即:只要在每个编译单元(文件)中编译的代码中,遇到被完整定义的模板,都会实例化。这就产生了重复实例化而导致的编译时间增长。而且我们没有办法通知编译器不要进行模版实例化。
C++引入了外部模版,扩充了原来的强制编译器在特定位置实例化模板的语法,使得能够显示地告诉编译器何时进行模板的实例化。
template class std::vector<MagicClass>; //强行实例化 extern template class std::vector<MagicClass>; // 不在该编译文件中实例化模板
八,类型别名模板
在了解类型别名模板之前,需要理解“模板”和“类型”之间的不同:模板是用来产生类型的。
在传统C++中,typedef可以为类型定义一个新的名称,但无法为模板定义一个新的名称。因为模板不是类型。
template <typename T, typename U> class SuckType; typedef SuckType<int, std::string> NewType; // 不合法
C++11中使用using来支持为模板定义一个新的名称,同时支持传统typedef的相同功效。
通常我们使用typedef定义别名的语法是:“typedef 原名称 新名称;”,但对函数指针等别名的定义语法却不相同,通常会给阅读造成一定程度的困难。
typedef int (*process)(void*); // 定义一个返回类型为int,参数为void)的函数指针类型,名称叫做process using process = int(*)(void *); // 同上,更加直观 using NewType = SuckType<int, std::string>;
使用using配合模版定义:
void add(int a, int b) { cout << "add" << endl; } template <typename T> using MyFunc = void(*)(T t, T u); // 只是定义了一个别名 int main(int, char *[]) { MyFunc<int> test; test = add; test(1, 2); system("pause"); return 0; }
也可以通过using定义任意类型的表达式:
template <typename T> using type_t = T; type_t<int> i = 10; // type_t<int>就是int
声明嵌套类型:
class MyClass { public: typedef int C; // 声明嵌套类型 using B = int; // 声明嵌套类型 }; int main(int, char *[]) { MyClass::C i1 = 10; cout << typeid(i1).name() << endl; // int MyClass::B i2 = 20; cout << typeid(i2).name() << endl; // int system("pause"); return 0; }
九,typedef和using对函数指针的别名使用
#include <iostream> #include <string> using namespace std; template <typename T, typename U> class MyClass; typedef int func_t_t(int, int); typedef int(*func_t_p)(int, int); typedef int(&func_t_r)(int, int); using func_u_t = int(int, int); using func_u_p = int(*)(int, int); using func_u_r = int(&)(int, int); int sum(int a, int b) { return a + b; } int sub(int a, int b) { return a - b; } func_t_p calc_t_p(char type) { if (type == '+') { return sum; } return sub; } func_u_p calc_t_r(char type) { if (type == '+') { return sum; } return sub; } func_t_p calc_u_p(char type) { if (type == '+') { return sum; } return sub; } func_u_r calc_u_r(char type) { if (type == '+') { return sum; } return sub; } int calc_t(char type, int a, int b) { func_t_p tp = calc_t_p(type); return tp(a, b); } int main(int, char *[]) { func_t_t * f_t_t = sum; func_t_p f_t_p = sum; func_t_r f_t_r = sum; func_u_t * f_u_t = sum; func_u_p f_u_p = sum; func_u_r f_u_r = sum; std::cout << f_t_t(1, 2) << std::endl; std::cout << f_t_p(1, 2) << std::endl; std::cout << f_t_r(1, 2) << std::endl; std::cout << f_u_t(1, 2) << std::endl; std::cout << f_u_p(1, 2) << std::endl; std::cout << f_u_r(1, 2) << std::endl; std::cout << calc_t_p('-')(1, 2) << std::endl; std::cout << calc_t_r('-')(1, 2) << std::endl; std::cout << calc_u_p('-')(1, 2) << std::endl; std::cout << calc_u_r('-')(1, 2) << std::endl; std::cout << calc_t('-', 1, 2) << std::endl; system("pause"); return 0; }
十,typdef用于结构体struct的区别
struct TagStruct // TagStruct是标记,在声明变量时使用,可以不存在 { int age; } aaa; //变量 typedef struct TagStruct2 // TagStruct2是标记,在声明变量时使用,可以不存在 { int age; } BBB; //BBB是类型名称,可以在变量声明时可以替代struct TagStruct2 aaa.age = 10; cout << "aaa.age:" << aaa.age << endl; struct TagStruct ccc; // 通过struct+标记,声明变量 ccc.age = 15; cout << "ccc.age:" << ccc.age << endl; BBB bbb; bbb.age = 20; cout << "bbb.age:" << bbb.age << endl; struct TagStruct2 ddd; ddd.age = 30; cout << "bbb.age:" << ddd.age << endl;
十一,->*与.*的示例
class MyClass { private: int age; public: MyClass(int a) :age(a) { } int GetAge() { return age; } int AddAge(int a) { return this->age + a; } }; int main(int, char *[]) { MyClass item(10); int temp = item.AddAge(5); cout << temp << endl; // print: 15 int (MyClass::*MyAdd)(int) = &MyClass::AddAge; // 指向成员的指针用于调用函数 temp = (item.*MyAdd)(2); cout << temp << endl; // print: 12 MyClass *newItem = &item; temp = (newItem->*MyAdd)(8); cout << temp << endl; // print: 18 system("pause"); return 0; }
十二,类的隐藏、覆盖和重载,using关键字使用
类的隐藏和重载不一样,
1、类的隐藏是指,一个类继承自另外一个类,则父类中与子类:
1)名称相同的函数,参数不同,将被隐藏,不管父类中这个函数重载了几次,子类中将不能再使用它们。
2)名称相同的函数,参数相同,但没有virtual关键字,也会被隐藏;有virtual关键字是覆盖。
2、类的覆盖是指,一个类继承自另外一个类,则父类中与子类名称相同、且参数相同、且父类函数必须声明virtual关键字;
3、类的重载是指,同一个类中,函数名称相同,参数不同,virtual关键字可有可无,的多个函数。
class Base { public: void printTest() { cout << "printTest..."<< endl; } void printTest(int i) { cout << "printTest int." << i << endl; } void printTest(double i) { cout << "printTest double." << i << endl; } void printABC() { cout << "printABC..." << endl; } private: void printDEF() { cout << "printABC..." << endl; } }; class Sub :Base { public: using Base::printTest; void printTest(string s) { cout << "printTest string." << s.c_str() << endl; } }; class Sub2 :private Base { public: using Base::printTest; }; void test() { Sub sub; sub.printTest(2); // 父类printTest(int i)被隐藏,使用using Base::printTest;可以访问 Sub2 sub2; sub2.printTest(2); // 私有继承,无法访问父类函数,使用using Base::printTest;可以访问 sub2.printTest(2.4); //sub2.printABC(); // 无法访问,没有使用using在子类中声明 //sub2.printDEF(); // 使用using也无法访问私有函数 } int main(int, char *[]) { test(); system("pause"); return 0; }
使用using可以访问private继承时父类非private函数。
十三,typename的特定用法
在模版定义中,typename与class相同。但以下用途是为了避免歧义:
template <class T> void foo() { typename T::iterator *iter; // 避免T中包含iterator静态变量引起的歧义。 }
这里typename声明T::iterator是一种类型,而不是变量。编译器在编译时就确定。否则可能好会被当成T::iterator*iter乘法表达式。使用typename的规则:
1)模板之外禁止使用,即typename只能用于模板定义中;
2)非限定类型禁止使用,比如int,vector<int>之类;
3)基类列表中禁止使用,比如template<class T> class C1: T::InnerType不能在T::InnerType前面加typename;
4)构造函数的初始化列表中禁止使用。
5)如果类型依赖于模型参数的限定名,那么在它之前就必须加typename(除非是基类列表,或者在类的初始化成员列表中)。
6)其它情况下typename是可选的,对于一个不依赖于名的限定名,typename可选。例如vector<int> vi;
7)对于不会引起歧义的地方,仍然需要加typename修饰;
template <class T> void foo() { typename T::iterator iter; // 避免T中包含iterator静态变量引起的歧义。 typedef typename T::iterator iterator_type; // 无歧义地声明嵌套类型 }
十四,使用std::initializer_list初始化列表
std::initializer_list是非常高效的,作为传参并不会耗费额外的性能。但我们应当总是把std::initializer_list看做保存对象的引用,并在它持有对象的生存周期结束前完成传递。另外std::initializer_list中的元素是只读的,不可修改。
#include <iostream> #include <string> #include <vector> #include <map> class FooVector { std::vector<int> pool; public: FooVector(std::initializer_list<int> list) { for (auto it = list.begin(); it != list.end(); ++it) { pool.push_back(*it); } } }; class FooMap { std::map<std::string, int> pool; using pair_t = std::map<std::string, int>::value_type; public: FooMap(std::initializer_list<pair_t> list) { for (auto it = list.begin(); it != list.end(); ++it) { pool.insert(*it); } } }; void func(std::initializer_list<int> list) { std::cout << "size:" << list.size() << std::endl; for (auto it = list.begin(); it != list.end(); ++it) { std::cout << *it << std::endl; } } // 不能将initializer_list作为返回值,因为它只保留了引用 std::initializer_list<int> getInitList() { int a = 1; int b = 2; return{a, b}; // 没有复制值 } std::vector<int> getVector() { int a = 1; int b = 2; return{ a, b }; } template <typename T> class Age { T age; public: Age(T i) { age = i; } T getAge() { return age; } }; template <typename T> class AgePool { std::vector<T> pool; public: AgePool(std::initializer_list<T> list) { for (auto it = list.begin(); it != list.end(); ++it) { pool.push_back(*it); } } void print() { for (auto t : pool) { std::cout << "age:" << t.getAge() << std::endl; } } }; void print() { } int main(int, char *[]) { FooVector fv{1, 2, 3, 4}; FooMap fm{ {"a", 1}, {"b", 2} }; func({1, 2, 3, 4, 5, 6}); auto a1 = getInitList(); std::cout << "getInitList size:" << a1.size() << std::endl; // print 2 for (auto it = a1.begin(); it != a1.end(); ++it) { std::cout << "getInitList:" << *it << std::endl; // 任意值 } auto a2 = getVector(); std::cout << "getVector size:" << a2.size() << std::endl; // print 2 for (auto it = a2.begin(); it != a2.end(); ++it) { std::cout << "getVector:" << *it << std::endl; // print 1, 2 } AgePool<Age<double>> ap{1, 2, 3, 4, 5}; // 批量Age对象初始化 ap.print(); system("pause"); return 0; }
十五,使用std::for_each
头文件:#include <algorithm>
#include <iostream> #include <string> #include <vector> #include <map> #include <algorithm> #include <memory> using namespace std; void print(int i) { cout << i << endl; } void test() { vector<int> v{ 1, 2, 3, 4, 5 }; std::for_each(v.begin(), v.end(), print); auto func = [](int i) { cout << i << endl; }; std::for_each(v.begin(), v.end(), func); } int main(int, char *[]) { test(); system("pause"); return 0; }
十六,逗号表达式
以括号为一个返回值的逗号表达式,会逐个执行,但只返回最后一个的值。
int a = (5, 6); cout << "a:" << a << endl; // print 6 int r = (a * 3, a * 4); cout << "r:" << r << endl; // print 24 int i1 = 0; int r2 = (++i1, 3, 5); // 会执行i1++ cout << "i1:" << i1 << endl; // print 1 cout << "r2:" << r2 << endl; // print 5 int i2 = ([](int x, int y) { return x+y; }(1, 2), 5, 6); // 第一个lambda执行,但只赋值最后一个 cout << "i2:" << i2 << endl; // print 6 int i3 = ((6, 7, 8), 9); // 第一个内嵌括号返回8,加上外部括号,返回9 cout << "i3:" << i3 << endl; // print 9 string i4 = ([] {return 1; }, "abc"); // 第一个只是lambda函数定义,没有执行 cout << "i4:" << i4.c_str() << endl; // print abc auto list1 = std::initializer_list<int>{ [&]() { return 1; }(), [&](int i) { return i; }(2), 3, 4}; // 第一、二个lambda执行 for (auto aa : list1) { cout << "list1-elem:" << aa << endl; // print 1 2 3 4 } int val = 2; auto list2 = std::initializer_list<int>{ ([&]() { cout << "lam val:" << val << endl; // no-print return val; }(), 6, 9), 7 }; for (auto aa : list2) { cout << "list2-elem:" << aa << endl; // print 9 7 } auto list4 = { []() { return 2; }(), 1 }; for (auto aa : list4) { cout << "list4-elem:" << aa << endl; // print 2 1 }
十七,可变模版遍历
#include <iostream> #include <string> #include <vector> #include <map> #include <algorithm> #include <memory> using namespace std; template <typename T> void printf(T t) { cout << "printT:"<< t << endl; } template <typename T, typename...Args> void printf(T t, Args... args) { cout << "printArgsSize:" << sizeof...(args) << endl; printf(args...); // 当args只有一个参数时,调用printf(T t);即跳出回调 } template <typename T, typename...Args> void printfList(T t, Args... args) { cout << "printf2:" << t << endl; // 利用逗号表达式,最后一项t为std::initializer_list<T>中T的推导,利用...执行多次,最后list为两个t值。所以list没用,重要的是利用这种方式遍历args... // T与Args可能不同类型 auto list = std::initializer_list<T>{([&] { cout << "lambda:" << args << endl; }(), t)...}; for (auto a : list) { cout << "list:" << a << endl; } } class MyWork { private: int base; public: MyWork(int i):base(i) { } void print() { cout << base << endl; } }; template <typename T, typename...Args> void testWork(T t, Args... args) { // T和Args都是相同类型 auto list = std::initializer_list<T>{t, ([&] { return args; }())... }; // 返回work1 work2 work3的集合 for (auto a : list) { a.print(); } } int main(int, char *[]) { printf(1, "abc", 3.5); printfList(2, "abc", 3.5); testWork<MyWork>(MyWork(1), MyWork(2), MyWork(3)); system("pause"); return 0; }
十八,可调用对象及包装器std::function
可调用对象定义:
1)是一个函数指针;
2)是一个具有operator()成员函数的类对象(仿函数);
3)是一个可以被转换为函数指针的类对象;
4)是一个类成员(函数)的指针;
#include <iostream> #include <string> #include <vector> #include <map> #include <algorithm> #include <memory> using namespace std; void func() { // ... } struct Foo { void operator()(void) // 仿函数 { // ... } }; struct Bar { static void func() { // ... } using fr_t = void(*)(void); operator fr_t(void) { return func; // 可被转换为函数指针的类对象 } }; struct A { int a_; void mem_func() // 类成员 { //... } }; int main(int, char *[]) { void(*func_ptr)(void) = &func; // 函数指针 func_ptr(); Foo foo; foo(); // 仿函数 Bar bar; bar(); // 可被转换为函数指针的类对象 void (A::*mem_func_ptr)(void) = &A::mem_func; // 类成员函数指针 int A::*mem_func_num = &A::a_; // 类成员指针 A aa; (aa.*mem_func_ptr)(); // 执行函数 aa.*mem_func_num = 13; // 给成员变量赋值 system("pause"); return 0; }
对上述可调用类型的定义,并没有包括函数类型或函数引用(只有函数指针),这是因为函数类型并不能直接用于定义对象,而函数引用从某种意义上来说,可以看做是一个const的函数指针。
std::function是可调用对象的包装器。它是一个类模板,可以容纳类除了成员(函数)指针以外的所有可调用对象。通过指定它的模板参数,从而用统一的方式处理函数、函数对象、函数指针,并允许保存或延迟执行它们。当给std::function合适的函数签名(即一个函数类型,只需要包括返回值类型和参数表类型),它就可以变成一个可以容纳所有这一类调用方式的“函数包装器”。
#include <iostream> #include <string> #include <functional> using namespace std; void func() { cout << __FUNCTION__ << endl; } struct Foo { int operator()(void) { cout << __FUNCTION__ << endl; return 0; } }; struct Bar { static int func(int a) { cout << __FUNCTION__ << endl; return 0; } }; class A { std::function<void()> callback_; // 封装回调函数 public: A(const std::function<void()>& f):callback_(f){} void callback() { callback_(); } int a_; int addOne(int i) { return i + 1; } }; void call_func(int x, const std::function<void(int)> f) // 作为参数传递 { f(x); } void print(int a) { cout << "print:" << a << endl; } int main(int, char *[]) { std::function<void(void)> fr1 = func; // 函数指针 fr1(); std::function<int(int)> fr2 = Bar::func; // 类的静态函数 int i = fr2(2); Foo foo; std::function<void(void)> fr3 = foo; // 仿函数 fr3(); A a(func); a.callback(); // 任何时候触发回调 call_func(4, print); // 传递函数指针 // lambda call_func(7, [](int i) { cout << "lambda:" << i << endl; }); // 使用bind来绑定函数和参数,就不需要function那样声明函数签名 auto bind1 = std::bind(print, std::placeholders::_1); call_func(5, bind1); auto bind2 = std::bind(print, std::placeholders::_1); bind2(10); auto bind3 = std::bind(print, 20); bind3(); // function+bind用于绑定类对象的非静态函数 std::function<int(int)> fb1 = std::bind(&A::addOne, &a, std::placeholders::_1); int r = fb1(1); cout << "r:" << r << endl; // print 2 // function+bind用于绑定类对象的成员变量 std::function<int&(void)> fb2 = std::bind(&A::a_, &a); fb2() = 123; cout << "a:" << a.a_ << endl; // print 123 system("pause"); return 0; }
十九,tuple元组及遍历
#include <iostream> #include <string> #include <tuple> using namespace std; struct A { int a; char* p; double b; }; template <typename T, int N> // 模板中可以定义变量 struct tuple_print { static void print(const T& t, std::ostream& os) { tuple_print<T, N-1>::print(t, os); // 递归,当N=2时,调用到特化模板 os << std::get<N - 1>(t) << endl; } }; template <typename T> struct tuple_print<T, 1> // 特化模板,不能放在struct tuple_print之前,且只支持类模板 { static void print(const T& t, std::ostream& os) { os <<"1:" <<std::get<0>(t) << endl; } }; template <typename... Args> std::ostream& operator<<(std::ostream& os, const std::tuple<Args...>& t) // 覆盖cout的operator<< { tuple_print<decltype(t), sizeof...(Args)>::print(t, os); return os; } int main(int, char *[]) { std::tuple<int, string, double> tp(1, "abc", 5.1); // 等价于结构体A cout << std::get<0>(tp) << endl; cout << std::get<1>(tp).c_str() << endl; cout << std::get<2>(tp) << endl; int x; string y; double z; std::tie(x, y, z) = tp; cout << x << endl; cout << y.c_str() << endl; cout << z << endl; auto tp2 = make_tuple(5, 6, 7); auto tt = std::tuple_cat(tp, tp2); // 拼接多个tuple cout << "------------------------------------" << endl; cout << tt << endl; system("pause"); return 0; }
相关推荐
C++学习笔记C++学习笔记C++学习笔记C++学习笔记C++学习笔记
C++学习笔记
### C++ 学习笔记精华版 #### 一、C++ 语言概述 **1、历史背景** - **C++ 的江湖地位** - Java、C、C++、Python、C# 是当前主流的编程语言之一,而 C++ 在这些语言中以其高效性和灵活性著称。 - **C++ 之父 ...
4. 栈和堆是程序中用于分配内存的两种方式。栈通常由编译器自动管理,具有固定的大小限制,并且存取速度快。堆内存分配通常由程序员控制,可以动态分配大小不一的内存块,但管理起来比较复杂,也容易产生内存碎片。 ...
《千锋C++笔记》是一份综合性的学习资料,涵盖了C++编程语言的基础到高级概念。这份笔记由知名教育机构“千锋”提供,旨在帮助初学者和有一定基础的程序员深入理解和掌握C++这一强大的系统级编程语言。下面将详细...
6. **C++11及以后的新特性**:C++11引入了许多新特性,如auto关键字、右值引用、lambda表达式、智能指针等,C++14和C++17进一步增强了这些特性,使得C++更加现代化和高效。 7. **内存管理**:理解指针是学习C++的...
C++核心学习笔记pdf
【C++课程笔记详解】 达内的C++课程是专为深入学习编程技术的学员设计的,特别是对于希望在计算机科学领域提升技能或者从事软件开发工作的人来说,这是一个宝贵的资源。本笔记涵盖的内容广泛,旨在帮助学生全面理解...
这份"C++笔记"包含了学习C++时的重要知识点和实践技巧。 1. **基础语法**:C++的基础包括变量、数据类型(如整型、浮点型、字符型等)、运算符(算术、比较、逻辑、位运算符等)、流程控制语句(如if-else、switch-...
"自考C++笔记(上)" 本笔记是作者全部手打创作的自考C++笔记,包含课本中例子的详细分析,共47200字,适合没有学过C语言的人认真学习和通过C++自考。 C++程序设计 ### 认识 C++的对象 #### 1.1 初识 ...
C++Primer中文第三版(C++从入门到精通)第一章的读书笔记,主要是C++程序、预处理器指示符、iostream库等的基础知识点读书笔记。
这份笔记主要涵盖了C++的基础知识,包括C++11和C++17的新特性,旨在帮助初学者理解C++的核心概念。 ### C++11特性 1. **引用(Reference)**:引用是一种别名机制,一旦引用被初始化为一个对象后,它就始终绑定到...
今天,让我们一起深入探究《黑马C++学习笔记》,这本涵盖了C++编程基础与核心概念的全面教材。 首先,C++的基础知识是每个学习者必须掌握的部分,这包括变量的声明与赋值、输出语句等基本操作。在C++中,变量的声明...
从给出的部分内容来看,读书笔记主要聚焦于以下几个知识点: 1. C++语言的联邦概念:C++是一个由多个次语言构成的语言联邦,这包括了C语言核心、面向对象的C++、模板C++以及标准模板库(STL)。这种理解对于深入...
C++ 学习笔记C++ 学习笔记C++ 学习笔记C++ 学习笔记002
根据给定的信息,我们可以从多个角度来探讨C++的相关知识点,包括但不限于集成开发环境的配置、数据类型初始化、命名空间的使用、类和对象的概念、引用类型的理解、指针的操作、函数重载以及内存管理等方面。...
C++是一种强大的面向对象编程语言,它源自C语言并扩展了其功能,包括类、模板、异常处理等高级特性。以下是对C++基础知识点的详细解释: 1. **空头程序**:C++中的空头程序是一个没有实际操作的简单程序,通常用于...
【C++学习笔记】这份详尽的资源是为那些希望深入了解C++编程语言的人们精心准备的。C++是一种强大的、通用的编程语言,它的设计理念是面向对象,同时支持过程化编程,使得它在系统软件、应用软件、游戏开发、设备...
4. **函数**:函数是C++中可重用的代码块,可以接受参数并返回值。函数封装了一定的功能,使得代码更加模块化。 5. **类和对象**:C++的核心是面向对象编程,类是对象的蓝图,定义了对象的属性(成员变量)和行为...
【达内C++课程笔记】是一份全面涵盖了C++编程语言以及相关技术的教程资源,由达内科技提供。这份笔记不仅包含C++的基础知识,还深入探讨了C++的高级特性和实际应用,旨在帮助学习者从入门到精通,掌握C++编程技能。 ...