C++2.0新特性

tiny_star Lv3

C++2.0新特性包括语言和标准库两个层面。

视频学习:侯捷老师 《C++2.0新特性》

标准库层面以header files(头文件)形式体现

C++标准库的header files不带副档名(.h),例如#include<vector>
新式C header files不带副名称.h,例如#include<cstdio>
旧式C header files(带有副名称.h)仍可用,例如#include<stdio.h>
标准库都放在命名空间std里面 ,使用标准库要么使用using namespace std或者std::

DevC++ 上的 ISO C++11 开关

Project -> Project Options -> Compiler -> Code Generation -> Language standard(-std)->选择 ISO C++11

资源网站

C++11 FAQ,from Stroustrup

CPlusPlus.com

CppReference.com

GCC, the GNU Compiler Collection - GNU Project

确认支持C++11:macro __cplusplus

1
std::cout<<__cplusplus;

输出199711则不支持C++11,输出201103及以上则支持C++11

Variadic Templates(数量不定的模板参数)

1
2
3
4
5
6
7
8
9
void print() // 无参数时调用,必须有
{
}
template<typename T, typename... Types>
void print(const T& firstArg, const Type&... args)
{
cout<<firstArg<<endl; //print first argument
print(args...); //call print() for remaining arguments
}

**…**就是一个所谓的pack(包)

用于 template parameters,就是 template parameters pack(模板参数包)
用于 function parameter types,就是 function parameter types pack(函数参数类型包)
用于 function parameters,就是 function parameters pack(函数参数包)

1
2
3
4
5
6
print(7.5, "hello", bitset<16>(377), 42);
// 输出结果为:
// 7.5
// hello
// 0000000101111001
// 42
1
2
3
4
// 与上面那个可以并存
template<typename... Types>
void print(const Types&... args)
{}

nullptr and std::nullptr_t(空指针)

C++11 lets you use nullptr instead of 0 or NULL
nullptr has type std::nullptr_t, defined in <cstddef>

Automatic Type Deduction with auto(自动类型推导)

With C++11, you can declare a variable or an object without specifying its specific type by using auto
Using auto is especially usefull where the type is a pretty long and/or complicated expression.

1
2
3
4
5
vector<string> v;
auto pos = v.begin(); // pos has type vector<string>::iterator
auto l = [](int x)->bool{ // l has the type of a lambda
// taking an int and returning a bool
};

Uniform Initialization(统一初始化/一致性初始化)

变量名后面加大括号{}

1
2
3
4
5
6
int values[]{1,2,3};
vector<int> v{2,3,5,7,11,13,17};
vector<string> cities{"Berlin","New York","London","Braunschweig","Cairo","Cologne"};
// 这形成一个 initializer_list<string>,背后有个 array<string,6> 。调用 vector<string>ctors 时编译器找到了一个 vector<string>ctor 接受 initializer_list<string> 。所有容器皆有如此 ctor
complex<double> c{4.0,3.0}; // equivalent to c(4.0,3.0)
// 这形成一个 initializer_list<double>,背后有个 array<double,2> 。调用 complex<double>ctor 时该 array 内的2个元素被分解传给 ctor。complex<double> 并无任何 ctor 接受 initializer_list<double>

编译器看到 {t1,t2…tn} 便做出一个initializer_list<T>(Tt1,t2…tn 类型),它关联至一个array<T,n>。调用函数(例如ctor)时该array内的元素可被编译器分解逐一赋给函数。但若函数参数是个initializer_list<T>,调用者却不能给予数个T参数然后以为它们会被自动转为一个**initializer_list<T>**传入。

Initializer Lists(初始化列表)

An initializer list forces so-called value initialization, which means that even local variables of fundamental data types, which usually have an undefined initial value, are initialized by zero (or nullptr, if it is a pointer)

1
2
3
4
int i;			// i has undefined value
int j{}; // j is initialized by 0
int* p; // p has undefined value
int* q{}; // q is initialized by nullptr

narrowing initializations——those that reduce precision or where the supplied value gets modified——are not possible with braces.

1
2
3
4
5
6
7
8
int x1(5.3);			// OK, but OUCH: x1 becomes 5
int x2 = 5.3; // OK, but OUCH: x2 becomes 5
int x3{5.0}; // ERROR/WARNING: narrowing conversion
int x4 = {5.3}; // ERROR/WARNING: narrowing conversion
char c1{7}; // OK: even though 7 is an int, this is not narrowing
char c2{99999}; // ERROR/WARNING: narrowing conversion (if 99999 doesn't fit into a char)
std:vector<int> v1 {1, 2, 4, 5 }; // OK
std:vector<int> v2 {1, 2.3, 4, 5.6 }; // ERROR/WARNING: narrowing conversion

initializer_list<>

std::initializer_list<> can be used to support initializations by a list of values or in any other place where you want to process just a list of values

1
2
3
4
5
6
7
8
void print(std::initializer_list<int> vals)		// 传给initializer_list者,一定是个initializer_list( or{...}形式)
{
for(auto p = vals.begin(); p != vals.end(); ++p) // a list of values
{
std::cout << *p << "\n";
}
}
print({12,3,5,7,11,13,17}); // pass a list of values to print()

initializer_list类源代码中,编译器会准备好array来调用私有的构造函数
The initializer_list object refers to the elements of this array without containing them
Copying an initializer_list object produces another object referring to the same underlying elements, not to new copies of them (reference semantics/引用语义).
如今所有容器都接受指定任意数量的值用于建构赋值insert()assign()max()min() 也愿意接受任意参数

1
2
3
4
cout << max( {string("Ace"), string("Stacy"), string("Sabrina"), string("Barkley")} );	// Stacy
cout << min( {string("Ace"), string("Stacy"), string("Sabrina"), string("Barkley")} ); // Ace
cout << max( {54, 16, 48, 5}); // 54
cout << min( {54, 16, 48, 5}); // 5

explicit for ctors taking more than one argument

禁止隐式转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class P
{
public:
P(int a, int b){
cout<<"P(int a, int b) \n";
}
P(initializer_list<int>){
cout<<"P(initializer_list<int>) \n"
}
explicit P(int a, int b, int c){
cout<<"explicit P(int a, int b, int c) \n";
}
};
void fp(const P&) {};

P p1(77, 5); // P(int a, int b)
P p2{77, 5}; // P(initializer_list<int>)
P p3{77, 5, 42}; // P(initializer_list<int>)
P p4={77, 5}; // P(initializer_list<int>)
P p5={77, 5, 42}; // [Error] converting to 'P' from initializer_list would use explicit constructor 'P::P(int a, int b, int c)'
P p6(77, 5, 42); // explicit P(int a, int b, int c)
fp({47, 11}); // P(initializer_list<int>)
fp({47, 11, 3}); // [Error] converting to 'const P' from initializer_list would use explicit constructor 'P::P(int a, int b, int c)'
fp(P{47, 11}); // P(initializer_list<int>)
fp(P{47, 11, 3}); // P(initializer_list<int>)

range-based for statement(基本范围的for循环)

1
2
3
4
5
6
7
8
9
10
11
12
for(decl : coll){				// 把coll中的元素一个个取出来赋值给decl
statement
}
// 编译器会把上面转换为下面两种中的一个
for(auto _pos=coll.begin(), _end=coll.end(); _pos!=_end; ++_pos){
decl = *_pos;
statement
}
for(auto _pos=begin(coll), _end=end(coll); _pos!=_end; ++_pos){ // both begin() and end() are global
decl = *_pos; // begin()和end()都是标准库中的全局函数
statement
}
1
2
3
4
5
6
7
vector<double> vec;
for(auto elem : vec){ // 不改变元素内容
cout<<elem<<endl;
}
for(auto& elem : vec){ // 改变元素内容,使用引用,更快
elem*=3;
}

no explicit type conversions are possible when elements are initialized as decl inside the for loop.(允许隐式转换)

1
2
3
4
5
6
7
8
9
10
class C
{
public:
explicit C(const string& s); // explicit(!) type conversion from strings
...
};
vector<string> vs;
for(const C& elem : vs) { // ERROR, no conversion from string to C defined
cout<<elem<<endl;
}

=default, =delete

如果你自行定义了一个ctor,那么编译器就不会再给你一个 default ctor
如果你强制加上 =default,就可以重新获得并使用 default ctor

1
2
3
4
5
6
7
8
9
10
11
12
class Zoo
{
public:
Zoo(int i1,int i2):d1(i1),d2(i2){ }
Zoo(const Zoo&)=delete; // 拷贝构造函数(用一个对象来初始化另一个对象)(按字节拷贝)
Zoo(Zoo&&)=default; // 移动构造函数
Zoo& operator=(const Zoo&)=default; // 赋值构造函数(operator=)(按字节拷贝)
Zoo& operator=(const Zoo&&)=delete; // 移动赋值函数
virtual ~Zoo(){ } // 析构函数
private:
int d1,d2;
}

Big-Three:拷贝构造函数,赋值构造函数,析构函数
Big-Five:拷贝构造函数,赋值构造函数,析构函数,移动构造函数,移动赋值函数
=default; 用于Big-Five之外无意义,编译报错
=delete; 可用于任何函数身上(=0只能用于 virtual 函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Foo
{
public:
Foo(int i):_i(i){}
Foo()=default; // 和上一个并存(ctor可以多个并存),默认构造函数

Foo(const Foo& x):_i(x._i){ }
Foo(const Foo&)=default; // [Error] 'Foo::Foo(const Foo&)' cannot be overloaded
Foo(const Foo&)=delete; // [Error] 'Foo::Foo(const Foo&)' cannot be overloaded

Foo& operator=(const Foo& x) {_i=x._i; return *this;}
Foo& operator=(const Foo& x)=default; // [Error] 'Foo& Foo::operator=(const Foo&)' cannot be overloaded
Foo& operator=(const Foo& x)=delete; // [Error] 'Foo& Foo::operator=(const Foo&)' cannot be overloaded

void func1()=default; // [Error] 'void Foo:func1()' cannot be defaulted,非Big-Five无默认函数
void func2()=delete; // ok,编译通过,但是意义不大

~Foo()=delete; // 必须出现在声明式,这会造成使用Foo object时出错 => [Error] use of deleted function 'Foo::~Foo()',出生命周期范围无法析构
~Foo()=default;

private:
int _i;
}

Foo f1(5);
Foo f2; // 如果没有写出默认构造函数=default;版本 => [Error] no matching function for call to 'Foo::Foo()'
Foo f3(f1); // 如果 copy ctor(拷贝构造函数)=delete; => [Error] use of deleted function 'Foo::Foo(const Foo&)'
f3=f2; // 如果 copy assign(赋值构造函数)=delete; => [Error] use of deleted function 'Foo& Foo::operator=(const Foo&)'

Private-Copy

1
2
3
4
5
6
7
8
9
10
11
class PrivateCopy{
private:
// no access specifier, following members are private by default
// copy control is private and so is inaccessible to ordinary user code
PrivateCopy(const PrivateCopy&);
PrivateCopy &operator=(const PrivateCopy&);
// other members
public:
PrivateCopy()=default; // use the synthesized default constructor
~PrivateCopy(); // users can define objects of this type but not copy them
}

class不允许被 ordinary user code copy, 但仍可被 friendsmembers copy。若欲完全禁止,不但必须把 copy controls 放到 private 内且不可定义之

Alias Template (template typedef)(别名模板)

1
2
3
4
5
6
template <typename T>
using Vec = std::vector<T, MyAlloc<T>>; // standard vector using own allocator

Vec<int> coll;
// 等价于
std::vector<int,MyAlloc<int>> coll;

It is not possible to partially or explicitly specialize an alias template(别名模板不能显式特化或偏特化)

1
2
3
4
5
6
7
8
9
// 使用macro(宏)无法达到相同效果
#define Vec<T> template<typename T> std::vector<T,MyAlloc<T>>
Vec<int> coll;
// 等价于
template<typename int> std::vector<int,MyAlloc<int>> coll; // 不符合

// 使用typedef亦无法达到相同效果,因为typedef是不接受参数的
// 至多写成这样:
typedef std::vector<int,MyAlloc<int>> Vec; // 不符合,类型无法改变

采用 function template + iterator + traits来实现向函数传入容器类型和容器中存储的类型

template语法在模板接受一个template参数Container时,当Container本身又是个class template,能取出Containertemplate参数
例如收到一个vector<string>,能够取出其元素类型string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
template<typename T>
void output_static_data(const T& obj)
{
cout<<...// static data of obj
}

template<typename Container>
void test_moveable(Container c)
{
typedef typename iterator_traits<typename Container::iterator>::value_type Valtype;
for(long i=0;i<SIZE;++i)
c.insert(c.end(), Valtype());
output_static_data(*(c.begin()));
Container c1(c);
Container c2(std::move(c));
c1.swap(c2);
}

test_moveable(list<MyString>());
test_moveable(list<MyStrNoMove>());

test_moveable(vector<MyString>());
test_moveable(vector<MyStrNoMove>());

test_moveable(deque<MyString>());
test_moveable(deque<MyStrNoMove>());
...
// 使用 RB-tree 时,元素需提供 operator<
// 使用 hashtable 时,元素需提供 operator< , hash function
// 使用 muti-容器时,元素还需提供 operator==

template template parameter(模板模板参数)

Alias templates are never deduced by template argument deduction when deducing a template template parameter
无法进行模板模板参数推导

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
template<typename T,template<class> class Container>
class XCls
{
private:
Container<T> c;
public:
XCls()
{
for(long i=0;i<SIZE;++i)
c.insert(c.end(),T());
output_static_data(T());
Container<T> c1(c);
COntainer<T> c2(std::move(c));
c1.swap(c2);
}
}

XCls<MyString, vector> c1; // [Error] expected a template of type 'template<class> class Container', got 'template<class _Tp, class _Alloc> class std::vector'
// Alias templates are never deduced by template argument deduction when deducing a template template parameter
// 无法进行模板模板参数推导


// 下面内容不得在 function body 之内声明
template<typename T>
using Vec=vector<T,allocator<T>>;

template<typename T>
using Lst=list<T,allocator<T>>;

template<typename T>
using Deq=deque<T,allocator<T>>;
// 上面内容不得在 function body 之内声明


XCls<MyString, Vec> c1;
XCls<MyStrNoMove, Vec> c2;

XCls<MyString, Lst> c3;
XCls<MyStrNoMove, Lst> c4;

XCls<MyString, Deq> c5;
XCls<MyStrNoMove, Deq> c6;
...
// 使用 RB-tree 时,元素需提供 operator<
// 使用 hashtable 时,元素需提供 operator< , hash function
// 使用 muti-容器时,元素还需提供 operator==

Type Alias (similar to typedef)

There is no difference between a type alias declaration and typedef declaration. This declaration may appear in block scope, class scope, or namespace scope.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
typedef void (*func)(int, int);
// identical to
using func=void(*)(int,int);

// the name 'func' now denotes a pointer to function (函数指针)
void example(int,int){}
func fn=example;

// type alias can introduce a member typedef name
template<typename T>
struct Container{
using value_type = T;
// identical to
typedef T value_type;
}

// which can be used in generic programming(泛型编程)
template<typename Cntr>
void fn2(const Cntr& c)
{
typename Cntr::value_type n;
}

一些关键字

noexcept(保证函数不抛异常)

1
2
3
void foo() noexcept;
// identical to
void foo() noexcept(true);

declares that foo() won’t throw. If an exception is not handled locally inside foo()——thus, if foo() throws——the program is terminated, calling std::terminate(), which by default calls std::abort()(函数抛出异常,调用该函数的函数不处理,异常会抛向调用该函数的函数的函数,若仍未处理,继续向上抛出,若一直未处理,到main函数(未处理异常)时将会调用abort函数结束程序)

You can even specify a condition under which a function throws no exception. For example, for any type Type, the global swap() usually is defined as follows: (可以限制一个条件,在该条件下,函数一定不会抛出异常)

1
2
3
4
void swap(Type& x,Type& y) noexcept(noexcept(x.swap(y))) // 该函数不抛出异常条件是x.swap(y)不抛出异常
{
x.swap(y);
}

Here, inside noexcept(…) , you can specify a Boolean condition under which no exception gets thrown: Specifying noexcept without condition is a short form of specifying noexcept(true)

You need to inform C++ (specifically std::vector) that your move constructor and destructor does not throw. Then the move constructor will be called when the vector grows. If the constructor is not noexcept, std::vector can’t use it, since then it can’t ensure the exception guarantees demanded by the standard
growable containers(会发生 memory reallocation)只有两种:vector 和 deque

1
2
3
4
5
6
7
8
9
10
11
12
class MyString{
private:
char* _data;
size_t _len;
...
public:
// move constructor
MyString(MyString&& str)noexcept : _data(str._data), _len(str._len) {...}
// move assignment
MyString& operator=(MyString&& str)noexcept {... return *this; }
...
}

using

using-directives for namespaces and using-declarations for namespace members
引入命名空间和引入命名空间中的成员,引入后,当前作用域内,使用不需要带命名空间前缀

1
2
3
using namespace std;

using std::count;

using-declarations for class members
引入类成员,引入后,当前作用域内,使用不需要带类前缀

1
using _Base::_M_impl;

type alias and alias template declaration (since C++11)
类型别名和别名模板

1
2
3
4
5
6
7
8
9
using func = void(*)(int,int);

template<typename T>
struct Container{
using value_type = T;
};

template<class CharT>
using mystring = std::basic_string<CharT,std::char_traits<CharT>>;

override

重写虚函数,接在要重写的函数后,未进行重写将报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
struct Base{
virtual void vfunc(float){ }
};

struct Derived1 : Base{
virtual void vfunc(int) {}
// accidentally create a new virtual function, when one intended to override a base class function
// This is a common problem, particularly when a user goes to modify the base class
};

struct Derived2 : Base{
virtual void vfunc(int) override {}
// [Error] 'virtual void Derived2::vfunc(int)' marked override, but does not override

//override means that the compiler will check the base class(es) to see if there is a virtual function with this exact signature
// And if there is not, the compiler will indicate an error

virtual void vfunc(float) override {}
};

final

禁用继承,使用在类后,表示当前类是最后一个,无法被继承,若继承将会报错

1
2
3
struct Base1 final {};

struct Derived : Base1 {}; // [Error] cannot derive from 'final' base 'Base1' in derived type 'Derived1'

禁用重写,使用在虚函数后,继承该类的类无法重写该函数

1
2
3
4
5
6
7
struct Base2{
virtual void f() final;
};

struct Derived2 : Base2 {
void f(); // [Error] overriding final function 'virtual void Base2::f()'
}

decltype ( defines a type equivalent to the type of an expression )

decltype是C++11新增的一个关键字,和auto的功能一样,用来在编译时期进行自动类型推导。

One application of decltype is to declare return types. Another is to use it in metaprogramming or to pass the type of a lambdadecltype应用有声明返回类型和用于元编程,或者传递lambda表达式类型)

decltype, used to declare return types

With C++11, you can alternatively declare the return type of a function behind the parameter list

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
template<typename T1,typename T2>
auto add(T1 x,T2 y) -> decltype(x+y);

template<typename T>
void test18_decltype(T obj)
{
map<string, float>::value_type elem1; // 当我们手上有type,可取其inner typedef

map<string, float> coll;
decltype(coll)::value_type elem2; // 面对obj取其class type的inner typedef

typedef typename decltype(obj)::iterator iType;
// 等价于
typedef typename T::iterator iType;

decltype(obj) anotherObj(obj);
}

test18_decltype(complex<int>()); // [Error]: complex类无迭代器,编译失败

decltype, used to pass the type of a lambda

面对 lambda ,我们手上往往只有 object ,没有 type
要获得其 type 就得借助于 decltype

1
2
3
4
auto cmp =[](const Person& p1,const Person& p2){
return p1.lastname()<p2.lastname() || (p1.lastname()==p2.lastname() && p1.firstname()<p2.firstname());
};
std::set<Person,decltype(cmp)> coll(cmp);

Lambdas

C++11 introduced lambdas, allowing the definition of inline functionality, which cna be used as a parameter or a local objecct. Lambdas change the way the C++ standard library is used
A lambda is a definition of functionality that can be defined inside statements and expressions
Thus, you can use a lambda as an inline function. The minimal lambda function has no parameters and simply does something
$$
mutable_{opt} throwSpec_{opt} -> retType_{opt} {…}
$$

1
2
3
[] {
std::cout<<"hello lambda"<<std::endl;
}

You can call it directly

1
2
3
[] {
std::cout<<"hello lambda"<<std::endl;
}(); // prints "hello lambda"

or pass it to objects to get called

1
2
3
4
auto l = [] {
std::cout<<"hello lambda"<<std::endl;
};
l(); // prints "hello lambda"

$$
mutable_{opt} throwSpec_{opt} -> retType_{opt} {…}
$$

[…] lambda introducer capture to access nonstatic outside objects inside the lambda. Static objects such as std::cout can be used
opt下标的 All of them are optional, but if one of them occurs, the parentheses for the parameters ( (…) ) are mandatory(强制的)
mutable objects are passed by value, but inside the function object defined by the lambda, you have write access to the passed value
retType Without any specific definition of the return type, it is deduced from the return value

You can specify a capture to access data of outer scope that is not passed as an argument
[=] means that the outer scope is passed to the lambda by value
[&] means that the outer scope is passed to the lambda by reference

1
2
3
4
int x=0;
int y=42;
auto qqq=[x,&y]{...};
// [=, &y] to pass by reference and all other objects by value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int id=0;
auto f=[id]()mutable{
std::cout<<"id:"<<id<<std::endl;
++id;// OK,如果没写mutable,不能++id
// 可以声明变量
static int x=5;
int y=6;
// 可以返回数值
return id;
};
//auto f=[id](){
// cout<<"id="<<id<<endl;
// ++id;// [Error] increment of read-only variable 'id'
//};

id=42;
f();
f();
f();
std::cout<<id<<std::endl;
// print
// id:0
// id:1
// id:2
// 42

The type of a lambda is an anonymous function object (or functor)

1
2
3
4
5
6
7
8
9
10
11
// 上面类似于下面
class Functor {
private:
int id; // copy of outside id
public:
void operator() () {
std::cout<<"id:"<<id<<std::endl;
++id; // OK
}
};
Functor f;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int id=0;
auto f=[&id](int param){
cout<<"id:"<<id<<endl;
++id;
++param;// OK
};

id=42;
f(7);
f(7);
f(7);
std::cout<<id<<std::endl;
// print
// id:42
// id:43
// id:44
// 45

C++ Keywords

  • Titre: C++2.0新特性
  • Auteur: tiny_star
  • Créé à : 2025-08-01 14:01:54
  • Mis à jour à : 2025-10-17 12:49:20
  • Lien: https://tiny-star3.github.io/2025/08/01/Cpp/C++2.0新特性/
  • Licence: Cette œuvre est sous licence CC BY-NC-SA 4.0.
Commentaires