Header files
C++2.0 新特性包括语言和标准库两个方面,后者以 header files 形式呈现。
- C++ 标准库的header files不带
.h
,例如: #include <vector> - 新式 C header files 不带
.h
,例如: #include<cstdio> - 旧式 C header files (带有
.h
) 仍可用,例如:#include <stdio.h>
编译器对 C++2.0 的支持
- C++11
#define __cplusplus 201103L
- C++98 & C++03
#define __cplusplus 199711L
编程实验
文件:Test.cpp
#include <iostream>
using namespace std;
int main()
{
cout << __cplusplus << endl;
return 0;
}
输出:
201103
Variadic Templates (可变参数模板)
- 《Primer C++》中文版第五版 918 页。
// 递归出口
void print()
{
}
template <typename T, typename... Types>
void print(const T &firstArg, const Types&... args)
{
cout << firstArg << endl; // 打印第一个参数
print(args...); // 对其余的参数调用 print
}
template <typename... Types>
void size(const Types&... args)
{
cout << "args number : " << sizeof...(args) << endl; // 产生参数数量
}
...
就是一个所谓的 pack 包
- 用于 template parameters, 就是 template parameters pack(模板参数包)
- 用于 function parameter types, 就是 function parameter types pack(函数参数类型包)
- 用于 function parameters,就是 function parameter pack (函数参数包)
编程实验
文件: main.cpp
#include <iostream>
#include <bitset>
using namespace std;
// 递归出口
void print()
{
}
template <typename T, typename... Types>
void print(const T &firstArg, const Types&... args)
{
cout << firstArg << endl; // 打印第一个参数
print(args...); // 对其余的参数调用 print
}
template <typename... Types>
void size(const Types&... args)
{
cout << "args number : " << sizeof...(args) << endl; // 获取参数数目
}
int main()
{
print(7.5, "hello", bitset<16>(377), 42);
cout << endl;
size(1,2,3,4,5,6);
return 0;
}
输出:
7.5
hello
0000000101111001
42
args number : 6
Spaces in Template Expressions
vector<list<int> >; // OK in each C++ version
verctor<list<int>>; // OK since C++11
nullptr and std::nullptr_t
C++11 lets youuse nullptr instead 0 or NULL
to specifya pointer refers to no value
(which differs from having an undifined value). This new feature especially helps toaviod mistakes that occurred when a null pointer was interpreted as an integral value
. For example:
(C ++ 11允许您使用nullptr而不是0或NULL来指定一个指针不指向任何值(这与具有未定义的值不同)。 此新功能特别有助于避免在将空指针解释为整数值时发生的错误。)
void f(int);
void f(void*);
f(0); // calls f(int)
f(NULL); // calls f(int) if NULL is 0, ambiguous otherwise
f(nullptr); // calls f(void*)
nullptr is a new keyword.It automatically converts into each pointer
type but not to any integral type. It has type std::nullptr_t, definedin <cstddef> (see Section 5.8.1,page 161), so you can now even overload operations for ths case that a null pointer is passed. Note that std::nullptr_tcounts as a fundamental data type
(see Section 5.4.2, page 127).
(nullptr是一个新关键字。 它会自动转换为每种指针类型,但不会转换为任何整数类型。 它的类型为std :: nullptr_t,在<cstddef>中定义(请参阅第5.8.1节,第161页),因此对于传递空指针的情况,您现在甚至可以重载操作。 请注意,std :: nullptr_t被视为基本数据类型(请参见第5.4节,第127页)。)
【标准库中 nullptr_t 的定义】文件:stddef.h
#if defined(_MSC_EXTENSIONS) && defined(_NATIVE_NULLPTR_SUPPORTED)
namespace std { typedef decltype(nullptr) nullptr_t; }
using ::std::nullptr_t;
编程实验
文件:Test.cpp
#include <iostream>
using namespace std;
void f(int i)
{
cout << "void f(int i) : " << i << endl;
}
void f(void *p)
{
cout << "void f(void *p) : " << p << endl;
}
int main()
{
f(0);
// f(NULL);
f(nullptr);
return 0;
}
输出:
void f(int i) : 0
void f(void *p) : 0
注: f(NULL) 编译输出
error: call of overloaded ‘f(NULL)’ is ambiguous
f(NULL);
^
test.cpp:5:6: note: candidate: void f(int)
void f(int i)
^
test.cpp:10:6: note: candidate: void f(void*)
void f(void *p)
Automatic Type Deduction with auto
With C++11, you can declare a variable or an object without specifying its specific type
by using auto. For example:
(使用C ++ 11,可以通过使用auto来声明变量或对象,而无需指定其特定类型。 例如:)
auto i = 42; // i has type int
double f();
anto d = f() // d has type double
Using auto is especially useful where the type is a pretty long and/or complicated expression
. For example:
vector<string> v;
auto pos = v.begin(); // pos has type vector<string>::iterator
auto I = [](int x)->bool{ // I has the type of a lambda
// ... // taking an int and return a bool
}
The latter is an object, representing a labda.
auto key
【标准库中 auto 的使用】文件:stl_iterator.h
#if __cplusplus >= 201103L
// DR 685.
inline auto
operator-(const __normal_iterator<_IteratorL, _Container>& __lhs,
const __normal_iterator<_IteratorR, _Container>& __rhs) noexcept
-> decltype(__lhs.base() - __rhs.base())
#else
inline typename __normal_iterator<_IteratorL, _Container>::difference_type
operator-(const __normal_iterator<_IteratorL, _Container>& __lhs,
const __normal_iterator<_IteratorR, _Container>& __rhs)
#endif
编程实验
文件:Test.cpp
#include <iostream>
#include <list>
#include <string>
#include <algorithm>
using namespace std;
void func1()
{
list<string> c = {"a", "b", "c"};
list<string>::iterator ite;
ite = find(c.begin(), c.end(), "a");
if (ite != c.end())
{
cout << *ite << endl;
}
}
void func2()
{
list<string> c = {"a", "b", "c"};
auto ite = find(c.begin(), c.end(), "a");
if (ite != c.end())
{
cout << *ite << endl;
}
}
int main()
{
func1();
func2();
return 0;
}
输出:
a
a
Uniform Initialization (统一初始化)
Before C++11, programmers, especially novices, could easily become confused by the question of how to initialize a variable or an object. Initialization could happen with parentheses, braces, and/or assignment operator
.
(在C ++ 11之前,程序员,尤其是新手,很容易对如何初始化变量或对象的问题感到困惑。 初始化可以使用括号 "()" ,花括号 "{}" 和/或赋值运算符 "=" 进行。)
Rect r1 = {3, 7, 20, 25, &area, &print};
Rect r2 = (3, 7, 20, 25);
int ia[6] = {27, 210, 12, 47, 109, 83};
For this reason, C++11 introduced the concept of uniform initialization, which means that for any initialization, you can use one common syntax, This syntax use braces, so the following is possible now:
(因此,C ++ 11引入了统一初始化的概念,这意味着对于任何初始化,你都可以使用一种通用语法,该语法使用大括号,因此现在可以进行以下操作:)
int values[] {1, 2, 3};
vector<int> v{2, 3, 5, 7, 11, 13, 17};
vector<string> cities {"Berlin", "New York"};
complex<double> c{4.0, 3.0}; // 相当于 c(4.0, 3.0)
【重要】发生了什么:
编译器看到 {t1, t2, ..., tn} 时便会做出一个 initializer_list<T>,它关联至一个 array<T, n>。此时有会出现两种可能:
- 调用函数(例如:构造函数)时该 array 内的元素可被逐一传给函数;
- 但若函数参数是个 initializer_list<T>, 调用者就不能给予数个 T 参数然后以为他们会被自动转换为一个 initializer_list<T> ”整包“传入。
- 例:
`vector<string> cities {"Berlin", "New York"}
;`
这形成一个 initializer_list<string>, 背后有个 array<string, 2>。调用 vector<string> ctors 时编译器找到了一个 vector<string> ctor 接受 initializer_list<T>,于是形成的initializer_list<string> 整包传入,不再进行分解(这也意味着,vector<string> cities ("Berlin", "New York");
是错误的,因为 "()"不会有 “initializer_list<T>包” 的形成)。所有容器皆有函数参数类型为 initializer_list<T> 的 cvtor。
- 例:
complex<double> c{4.0, 3.0};
这形成一个initializer_list<double>, 背后有个 array<double, 2>。 complex<T> 类无函数参数类型为 initializer_list<T> 的 ctor,调用 complex<double> ctor 时,该 array 内的两个元素被分解依次传给 ctor。
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 nullpre, if it is a pointer).
(初始化程序列表会强制进行所谓的值初始化,这意味着即使基本数据类型的局部变量(通常具有未定义的初始值)也将被初始化为零(如果为指针,则为nullpre)。)
void func()
{
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
}
Note, however, that narrowing initializations -- those that reduce precision or where the supplied value gets modified -- are not possible with braces. For example:
(但是,请注意,使用大括号不可能缩小初始化范围(这些初始化会降低精度或修改提供的值)。例如:)
int x1(5.3); // OK, but OUCH: x1 becomes 5
int x2 = 5.3; // OK, but OUCH: x3 becomes 5
int x3{5.0}; // ERROR: narrowing
int x4 = {5.3};`// ERROR: narrowing
char c1{7}; // OK: even 7 is an int, this is not narrowing
char c2{9999}; // ERROR: narrowing (if 9999 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: narrowing
编程实验
文件:Test.cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void func1()
{
int i;
int j{};
int *p;
int *q{};
cout << i << ' ' << j << ' ' << p << ' ' << q << endl;
}
void func2()
{
int x1(5.3);
int x2 = 5.3;
// int x3{5.0}; // error: narrowing conversion of ‘5.0e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
// int x4 = {5.3}; // error: narrowing conversion of ‘5.2999999999999998e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
char c1{7};
// char c2{9999}; // error: narrowing conversion of ‘9999’ from ‘int’ to ‘char’ inside { } [-Wnarrowing]
cout << x1 << ' ' << x2 << ' ' << c1 << endl;
}
void func3()
{
vector<int> v1 {1, 2, 4, 5};
// vector<int> v2 {1, 2.3, 4, 5.6}; // error: narrowing conversion of ‘2.2999999999999998e+0’ from ‘double’ to ‘int’ inside { } [-Wnarrowing]
for (const auto &elem : v1)
{
cout << elem << ' ';
}
cout << endl;
}
int main()
{
func1();
func2();
func3();
return 0;
}
输出:
21865 0 0x55605c8e2ae0 0
5 5
1 2 4 5
initializer_list <>
To support the concept ofinitializer lists for user-defined types
.C++ provides the class template std::initializer_list<>. It can be used to support initializationsby a lis
t of values or in any other other placewhere you want to peocess just a list of values
. For example:
(支持用户定义类型的初始化列表的概念。C++提供类模板std::initializer_list<>。它可以用于支持通过值列表进行初始化,也可以用于你只想处理值列表的任何其他地方。例如:)
编程实验
文件:Test.cpp
#include <iostream>
using namespace std;
void print(std::initializer_list<int> vals)
{
for (auto p = vals.begin(); p!=vals.end(); ++p)
{
std::cout << *p << "\n";
}
}
int main()
{
print({12, 3, 5, 7});
return 0;
}
输出:
12
3
5
7
注: 传给 initializer_list 者,一定也必须是个 initializer_list (or {...} 形式)。
When there constructors for botha specific number of arguments
andan initializer list
, this version with the initializer list is preferred.
(当既有特定数量的参数又有初始化器列表的构造函数时,首选带有初始化器列表的版本。)
编程实验
文件:Test.cpp
#include <iostream>
using namespace std;
class P
{
public:
P(int a, int b)
{
cout << "P(int a, int b), a=" << a << ", b=" << b << endl;
}
P(initializer_list<int> initlist)
{
cout << "P(initializer_list<int> initlist), values=";
for (const auto &i : initlist)
{
cout << i << ' ';
}
cout << endl;
}
};
int main()
{
P p(77, 5);
P q{77, 5};
P r{77, 5, 42};
P s={77, 5};
return 0;
}
输出:
P(int a, int b), a=77, b=5
P(initializer_list<int> initlist), values=77 5
P(initializer_list<int> initlist), values=77 5 42
P(initializer_list<int> initlist), values=77 5
Without the constructor for the initializer list, theconstructor taking two ints
would be called to initialize q and s, while the initialization ofr would be invalid
.
(如果没有用于初始化列表的构造函数,则将采用两个整数的构造函数来初始化q和s,而r的初始化将无效。)
【标准库 initailizer_list 源码】 文件: initailizer_list
/// initializer_list
template<class _E>
class initializer_list
{
public:
typedef _E value_type;
typedef const _E& reference;
typedef const _E& const_reference;
typedef size_t size_type;
typedef const _E* iterator;
typedef const _E* const_iterator;
private:
iterator _M_array;
size_type _M_len;
// The compiler can call a private constructor.
constexpr initializer_list(const_iterator __a, size_type __l)
: _M_array(__a), _M_len(__l) { }
public:
constexpr initializer_list() noexcept
: _M_array(0), _M_len(0) { }
// Number of elements.
constexpr size_type
size() const noexcept { return _M_len; }
// First element.
constexpr const_iterator
begin() const noexcept { return _M_array; }
// One past the last element.
constexpr const_iterator
end() const noexcept { return begin() + size(); }
};
注:constexpr initializer_list(const_iterator __a, size_type __l) : _M_array(__a), _M_len(__l) { }
- 编译器有无上权力调用私有构造函数;
- 构造函数调用前,已经有一个 array 准备好。
Array
TR1 array 实现 【内使用原生数组进行数据存储】
G4.9 array 实现 (与 TR1 中实现主体原理相同)
文件:array
template<typename _Tp, std::size_t _Nm>
struct __array_traits
{
typedef _Tp _Type[_Nm];
typedef __is_swappable<_Tp> _Is_swappable;
typedef __is_nothrow_swappable<_Tp> _Is_nothrow_swappable;
static constexpr _Tp&
_S_ref(const _Type& __t, std::size_t __n) noexcept
{ return const_cast<_Tp&>(__t[__n]); }
static constexpr _Tp*
_S_ptr(const _Type& __t) noexcept
{ return const_cast<_Tp*>(__t); }
};
// ...
template<typename _Tp, std::size_t _Nm>
struct array
{
typedef _Tp value_type;
typedef value_type* pointer;
typedef const value_type* const_pointer;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef value_type* iterator;
typedef const value_type* const_iterator;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
// Support for zero-sized arrays mandatory.
typedef _GLIBCXX_STD_C::__array_traits<_Tp, _Nm> _AT_Type;
typename _AT_Type::_Type _M_elems;
// No explicit construct/copy/destroy for aggregate type.
// DR 776.
void
fill(const value_type& __u)
{ std::fill_n(begin(), size(), __u); }
void
swap(array& __other)
noexcept(_AT_Type::_Is_nothrow_swappable::value)
{ std::swap_ranges(begin(), end(), __other.begin()); }
// Iterators.
_GLIBCXX17_CONSTEXPR iterator
begin() noexcept
{ return iterator(data()); }
_GLIBCXX17_CONSTEXPR const_iterator
begin() const noexcept
{ return const_iterator(data()); }
_GLIBCXX17_CONSTEXPR iterator
end() noexcept
{ return iterator(data() + _Nm); }
_GLIBCXX17_CONSTEXPR const_iterator
end() const noexcept
{ return const_iterator(data() + _Nm); }
...
}
P p(77, 5); // P(int a, int b), a=77, b=5
P q{77, 5}; // P(initializer_list<int> initlist), values=77 5
P r{77, 5, 42}; // P(initializer_list<int> initlist), values=77 5 42
P s={77, 5}; // P(initializer_list<int> initlist), values=77 5
initializer_list object are automatically constructed as if an array of elements of type T was allocated, with each of the elements in the list being copy-initialized to its corresponding element in the array, using necessary non-narrowing implicit conversions.
(将自动构造initializer_list对象,就像分配了类型T的元素数组一样,使用必要的非缩小隐式转换将列表中的每个元素复制初始化为数组中的相应元素。)通俗理解:"拆包"逐一"分发".
The initializer_list object refers to the elements of this array without containing them
: copying an initializer_list produces another object referring to the same underlying elements, not to new copies of them (reference semantics).
(initializer_list对象引用此数组的元素但不包含它们:复制initializer_list会生成另一个对象,该对象引用相同的基础元素,而不引用它们的新副本(引用语义)。) (通俗理解:对象复制时,将发生浅拷贝,两对象指向相同的底层元素)
如今所有容器都接受任意数量的值用于构造或赋值或insert或assign;max 和 min 也接受任意参数。
编程实验
文件:Test.cpp
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
vector<int> v1 {2, 5, 7, 13, 69, 83, 50};
vector<int> v2 ( {2, 5, 7, 13, 69, 83, 50} );
vector<int> v3;
v3 = {2, 5, 7, 13, 69, 83, 50};
v3.insert(v3.begin() + 2, {0, 1, 2, 3, 4});
for (auto i : v3)
{
cout << i << ' ';
}
cout << endl;
cout << max({string("Ace"), string("Sracy"), string("Sabrina"), string("Barkley")}) << endl;
cout << min({string("Ace"), string("Sracy"), string("Sabrina"), string("Barkley")}) << endl;
cout << max({54, 16, 48, 5}) << endl;
cout << min({54, 16, 48, 5}) << endl;
return 0;
}
输出:
2 5 0 1 2 3 4 7 13 69 83 50
Sracy
Ace
54
5
注:不同操作所对应的函数原型实现.
文件:vector
文件:std_algo.h
vector<int> v1 {2, 5, 7, 13, 69, 83, 50};
vector<int> v2 ( {2, 5, 7, 13, 69, 83, 50} );
==>
vector(initializer_list<value_type> __l, const allocator_type& __a = allocator_type())
: _Base(__a)
{
_M_range_initialize(__l.begin(), __l.end(), random_access_iterator_tag());
}
v3 = {2, 5, 7, 13, 69, 83, 50};
==>
vector& operator=(initializer_list<value_type> __l)
{
this->_M_assign_aux(__l.begin(), __l.end(), random_access_iterator_tag());
return *this;
}
v3.insert(v3.begin() + 2, {0, 1, 2, 3, 4});
==>
void insert(iterator __position, size_type __n, const value_type& __x)
{
_M_fill_insert(__position, __n, __x);
}
cout << max({54, 16, 48, 5}) << endl;
cout << min({54, 16, 48, 5}) << endl;
==>
template<typename _Tp>
_GLIBCXX14_CONSTEXPR
inline _Tp
max(initializer_list<_Tp> __l)
{ return *std::max_element(__l.begin(), __l.end()); }
template<typename _Tp, typename _Compare>
_GLIBCXX14_CONSTEXPR
inline _Tp
max(initializer_list<_Tp> __l, _Compare __comp)
{ return *std::max_element(__l.begin(), __l.end(), __comp); }
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。