C++11
空格
vector<list<list> >; // C++11之前需要一个空格
vector<list<list>>; // OK 在C++11之后
nullptr和std::nullptr_t
// 空指针可以设置成 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的类型
decltype
// 用于定义变量而不必关注类型
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"};
initializer lists (值得重看)
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个元素的最小值,现在可以求不限个数了
explict
// 用途很少,主要用在构造函数, 防止隐式转换,下面是例子
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循环
for (decl : coll)
{ statement; }
=default 和 =delete
以下面的例子为例:
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则
Alias(别名) Template (template typedef)
原本可以用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就是化名
noexcept
void foo() noexcept; // void foo() noexcept(true);
override
用在虚函数身上,强制让函数签名完全相同。
final
用在2个地方:
- 告诉编译器这个类不能被继承。
- 告诉编译器这个函数不能override。
Lambdas
定义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的函数体必须不能太复杂,否则报错之后晦涩的错误信息将会极度地降低效率。
Variadic Template
例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!
尽管如此,临时对象还是不能放在左边。
待更新~