vector<list<list> >; // C++11之前需要一个空格 vector<list<list>>; // OK 在C++11之后
// 空指针可以设置成 nullptr void f(int); void f(void*); f(0); // call f(int) f(NULL); // call f(int) if NULL == 0 f(nullptr); // call f(void*) typedef decltype(nullptr) nullptr_t; // 所以nullptr_t就是nullptr的类型
// 用于定义变量而不必关注类型 int i; decltype(i) a;
auto a = {1,2}; // a是一个int类型,用于模板和lambda函数的返回值等复杂类型的情况
变量初始化的 标准化,全部都可以用大括号初始化变量,幕后是array结构在支撑
int val[] {1, 2, 3}; vector<int> vec{1, 2, 3}; vector<string> cites{"a", "a", "a", "a", "a", "a"};
int i; // i有未定义的值 int j{}; // j 初始化成0 int* p; // p有未定义的值 int* q{}; // q被初始化成nullptr int x1(5.1); // OK,但是x1会是5 int x2 = 5.3; // OK,但是x2会是5 int x3{5.3}; // ERROR,强制转换失败 int x4 = {5.3}; // ERROR,强制转换失败 class P { P(int a, int b) { cout << a << b << endl; } P(initializer_list<int> il) { for (auto &i :il) cout << i << " "; cout << endl; } } P p(3, 5); // 调用P(int a, int b) P p{3, 5}; // 调用P(initializer_list il),但是没有这个函数的时候就调用另一个构造函数了 P p{1, 3, 6}; // 调用P(initializer_list il) int a = min{1, 3, 5}; // 原来的min版本只能求2个元素的最小值,现在可以求不限个数了
// 用途很少,主要用在构造函数, 防止隐式转换,下面是例子 Class Complex { int real, img; explict /* 告诉编译器要执行严格的类型转换 */ Complex(int re, int im = 0) : real(re), img(im) {} Complex operator+(const, Complex& x) { return Complex(real + x.real, img + x.img);} };
for (decl : coll) { statement; }
以下面的例子为例:
Class Zoo { public: Zoo(int il1, int il1) : il1(i1), il2(i2) {} Zoo(const Zoo&) = delete; Zoo(Zoo&&) = default; Zoo& operator = Zoo(const Zoo&) = default; Zoo& operator = Zoo(const Zoo&&) = delete; vitual ~Zoo(); private: int i1, i2; };
原来的情况是如果你写了上面的一个类,但是没有定义任何的ctor(包括无参构造函数、析构函数、拷贝构造函数、参数是引用的move构造函数、赋值构造函数和参数是引用的move构造函数),C++编译器将会帮你写出来;但是只要你主动声明了这些函数,C++编译器就不会做这个事情。
那么现在如果对其中的某个函数加上了delete就表示不需要这个函数。 而如果加上了delete就表示需要这个函数。
btw:如果default用在其他函数的话,没有意义,只会编译报错;如果delete则
原本可以用typedef就有相似的功能,可是有无法传递参数的短板。
现在用using创建别名,就不会有这样的短板。
当然,不仅与此,别名可以处理模板的模板参数,二阶模板参数。
值得回味。
还是using。
typedef void (*)(int, int);
变成using func = void (*)(int, int)
template<class CharT> using mystring = std::basic_string<CharT, std::char_trais<CharT>>; mystring<char> str; // mystring就是化名
void foo() noexcept; // void foo() noexcept(true);
用在虚函数身上,强制让函数签名完全相同。
用在2个地方:
定义inline函数(仿函数、函数对象)。
简单的例子:
[]{std::out << "hello world" << std::endl;} // why not call it directly []{std::out << "hello lambda" << std::endl;}() // print lambda auto I = []{std::out << "hello lambda 2" << std::endl;} // 得到函数对象 I(); // print lambda 2 // 完整形式 [...](...)mutable throwSpec -> retType {...} // mutable 关系到方括号内的数据是否可以被改写 // 表示可以产生异常 // 向右的箭头后面代表返回类型 // 小括号里是参数 // 中括号代表这是lambda函数开始 // 中括号里面的内容 // 如果 mutable、throwSpec和ret都不存在,那么括号可以不写。 // 方括号[]内如果是=等号,就表示以值(而且是常量不能改变)的方式捕获外部变量;如果是&就表示以引用的方式捕获外部变量。 // lambda的函数体必须不能太复杂,否则报错之后晦涩的错误信息将会极度地降低效率。
例1:
void printX() {} template<typename T, typename... Type> void printX(const T& firstArg, const Type&... args) { cout << firstArg << endl; // 打印第一个参数 printX(args...); } printX(7.5,"hello",bitset<16>(377),42); // 此时如果执行这一行 // 就相当对第8行的print函数中的参数,逐一使用函数模板,结果就输出 // 第8行函数中的所有参数
例2:使用这个特性重写printf库函数。
void printf(const char *s) { while (*s) { if (*s == '%' && *(s++) == '%') { throw std::runtime_error("invalid format string: missing"); std::cout << *s++; } } } template<typename T, typename... Args> void printf(const char *s, T value, Args... args) { while (*s) { if (*s == '%' && *(s++) == '%') { std::cout << value; printf(++s, args...); // call even *s == 0 return; } } }
例3:实现maximun,一些值的最大值。
注:用初始化列表也可以,所以只是实例,不是说这个例子是最佳实例。
cout << max({1, 2, 3 4}) << endl;
例4:实现maximun,一些值的最大值
cout << maximum(57, 48, 60, 100, 20, 18) << endl; int maximum(int n) { return n; } template<typename... Args> int maximum(int n, Args... args) { return std::max(n, maximum(args...)); }
例5:使用类模板(用不同一般的方式使用first元素和last元素)
。。。没看明白
例6:用于递归继承的类模板
template<typename... Values> class tuple; template<> class tuple<> {}; /////////////////////////////////////////// template<typename Head, typename... Tail> class tuple<Head, Tail...> : private tuple<Tail...> { typedef tuple<Tail...> inherited; protected: Head m_head; // 位置在head()函数必须要前面head()函数才能用成员变量m_head public: tuple(){} tuple(Head v, inherited(vtail...)):m_head(v), inherited(vtail..) {} // Head::type 编译不通过,不是所有类型都能::type // typename Head::type head() { return m_head; } auto head()->decltype(m_head) { return m_head; } inherited& tail() { return *this; } } // run tuple<int, float, string> t(41, 6.3, "nico"); cout << sizeof(t) << endl; cout << t.head() << endl; cout << t.tail().head() << endl; cout << t.tail().tail().head() << endl; // 结果是分行输出 41 6.3 nico
例7:用于复合的递归
和例6相似。
新的类型,TR1后就有,但是大家不熟悉。
用来避免一些不必要的copy。
表达式都是右值,
不能被做赋值操作的都是右值,例如a+1就是右值, 这个代码就是不成立的a+1=a。但是:
string s1("aaa"); string s2("bbb"); s1 + s2 = s1; // just OK?! cout << s1 << endl << s2 << endl; string() = "ccc"; // OK too?! // try complex complex<int> c1(3, 8), c2(1, 0); c1 + c2 = complex<int>(41, 9); // c1 + c2 could be lvalue complex<int>() = complex<int>(3, 1); // ok too! temp obj could be assigned to sth!
尽管如此,临时对象还是不能放在左边。
待更新~