1

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 (函数参数包)

1_meitu_1.jpg

2_meitu_2.jpg

编程实验

文件: 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 you use nullptr instead 0 or NULL to specify a pointer refers to no value(which differs from having an undifined value). This new feature especially helps to aviod 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_t counts 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>。此时有会出现两种可能:

  1. 调用函数(例如:构造函数)时该 array 内的元素可被逐一传给函数;
  2. 但若函数参数是个 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 of initializer lists for user-defined types.C++ provides the class template std::initializer_list<>. It can be used to support initializations by a list of values or in any other other place where 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 both a specific number of arguments and an 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, the constructor taking two ints would be called to initialize q and s, while the initialization of r 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) { }

  1. 编译器有无上权力调用私有构造函数;
  2. 构造函数调用前,已经有一个 array 准备好。

Array

TR1 array 实现 【内使用原生数组进行数据存储】

1_meitu_1.jpg

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 也接受任意参数。

2_meitu_2.jpg

编程实验

文件: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); }

TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧