C++11 New Features

Table of Contents

auto

auto i = 42;        // i is an int
auto l = 42LL;      // l is an long long
auto p = new foo(); // p is a foo*

std::map<std::string, std::vector<int>> map;
for (auto it = begin(map); it != end(map); ++it) {
}

auto 不能直接作为函数的返回类型,但可以通过尾随指定类型,如下:

template <typename T1, typename T2>
auto compose(T1 t1, T2 t2) -> decltype(t1 + t2) {
  return t1+t2;
}
auto v = compose(2, 3.14); // v's type is double

nullptr

0被用来表示null指针,所以有与整型的隐式转换的缺点. C++11中类型是 std::nullptr_tnullptr 来表示null指针. 隐式转换存在从 nullptr 到 任何类型的null指针,并且可以到 bool 类型(也就是 false ). 但是与整型的隐式转换不存在.向后兼容,0仍然是有效的null指针值.

typedef decltype(nullptr) nullptr_t;
void foo(int* p) {}

void bar(std::shared_ptr<int> p) {}

int* p1 = NULL;
int* p2 = nullptr;   
if (p1 == p2) {
}

foo(nullptr);
bar(nullptr);

bool f = nullptr;
int i = nullptr; // error: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type

loops

#include <iostream>
#include <map>
#include <vector>
using namespace std;

int main() {
  std::map<std::string, std::vector<int>> map;
  std::vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  map["one"] = v;

  for (const auto& kvp : map) {
    std::cout << kvp.first << std::endl;

    for (auto v : kvp.second) {
      std::cout << v << std::endl;
    }
  }

  int arr[] = {1,2,3,4,5};
  for (int& e : arr) {
    e = e*e;
  }
  for (int& v : arr) {
    cout << v << endl;
  }
}

Override and final

在多类中实现多态时,需要使用 virtual 关键字来实现,并且要通过类层次来确定是否覆盖成功,或者想覆盖却函数声明有一些不同等不易发现的错误,如下:

class B 
{
 public:
  virtual void f(short) {std::cout << "B::f" << std::endl;}
};

class D : public B
{
 public:
  virtual void f(int) {std::cout << "D::f" << std::endl;}
};

本意想 D::f 覆盖 B::f,但是它们的参数不同,使得覆盖失败, B::f 仅仅是另外个虚函数.

另外如下的小错误,在基类中,函数被 const 标识,但继承类中的却没有.所以基类的虚函数不会被继承类的覆盖.

class B 
{
 public:
   virtual void f(int) const {std::cout << "B::f " << std::endl;}
};

class D : public B
{
 public:
   virtual void f(int) {std::cout << "D::f" << std::endl;}
};

在C++11中可以显示的描述你的多态目的. override 表明一个方法是基类虚函数的覆盖, final 表明虚函数不能覆盖此表示的函数.

如下例子,意图 D::f 覆盖基类的 B::F:

#include <iostream>
using namespace std;

class B {
 public:
  virtual void f(short) {std::cout << "B::f" << std::endl;}
};

class D : public B {
 public:
  virtual void f(int) override {std::cout << "D::f" << std::endl;}
};

int main() {
  B* base = new D();
}

编译,出现如下错误:

./overrride.cc:11:16: error: ‘virtual void D::f(int)’ marked override, but does not override

如下例子,意图 D::f 不让之后的继承类的函数覆盖:

#include <iostream>
using namespace std;

class B  {
 public:
   virtual void f(int) {std::cout << "B::f" << std::endl;}
};

class D : public B {
 public:
   virtual void f(int) override final {std::cout << "D::f" << std::endl;}
};

class F : public D {
 public:
   virtual void f(int) override {std::cout << "F::f" << std::endl;}
};


int main() {
  B* base = new F();
  return 0;
}

编译,出现如下错误:

./final.cc:16:17: error: virtual function ‘virtual void F::f(int)’
./final.cc:11:17: error: overriding final function ‘virtual void D::f(int)’

Strongly-typed enums

传统的C++的enum有一些弊端: enumerators被暴露在所在的范围,如果同一范围定义的enumerators拥有相同的名字,那么就会造成命名冲突,并且它们隐式的被转换为整型.

C++11引入新的enums类别,叫做 strongly-typed enums. 由 enum class 特定. 它们不在把enumerators暴露到所在代码范围,并不在隐式转换成整型,如下:

enum class Options {None, One, All};
Options o = Options::All;

Smart pointers

C++11中引入如下三种smart pointer, 在头文件 <memory> 中:

  • unique_ptr : 当内存资源不可共享时使用(没有copy constructor),但可以转移给另一个 unique_ptr.
  • shared_ptr : 当拥有的内存资源可共享时使用.
  • weak_ptr : 拥有对由 shared_ptr 管理的对象的reference,但不对 reference count影响.

unique_ptr

unique_ptr 之间对对象的拥有权的转让通过 std::move 操作, 被转让的 unique_ptr 成为 null, get() 操作返回 nullptr.

#include <iostream>
#include <memory>

void foo (int* p) {
   std::cout << *p << std::endl;
}

int main() {
  std::unique_ptr<int> p1(new int(42));
  std::unique_ptr<int> p2 = std::move(p1); // transfer ownership

  if (p1) {
    foo(p1.get());
  } else {
    std::cout << "p1 is null" << std::endl;
  }

  (*p2)++;

  if(p2)
    foo(p2.get());
  return 0;
}

shared_ptr

#include <iostream>
#include <memory>

void foo(int* p) {
  std::cout << "foo: " << *p << std::endl;
}
void bar(std::shared_ptr<int> p) {
  ++(*p);
}

int main() {
  std::shared_ptr<int> p1(new int(42));
  std::shared_ptr<int> p2 = p1;

  bar(p1);
  foo(p2.get());
  return 0;
}

第一句创建 p1 也可以写成如下:

auto p1 = std::make_shared<int>(42);

make_shared 不是成员函数,具有为共享对象和智能指针分配一步分配完成的优势.而显示的创建 shared_ptr 通过构造对象需要两步分配来完成.除了可能的多余开销,并可能在某些情况下造成memory leaks.如下例子,若 seed() throw错误,那么有可能发生memory leaks.

void foo(std::shared_ptr<int> p, int init) {
   *p = init;
}
foo(std::shared_ptr<int>(new int(42)), seed());

weak_ptr

weak_ptr 通过 lock() 操作获得此对象的 shared_ptr .

#include <iostream>
#include <memory>

int main() {
  auto p = std::make_shared<int>(42);
  std::weak_ptr<int> wp = p;
  {
    auto sp = wp.lock();
    std::cout << *sp << std::endl;
  }
  p.reset();

  if(wp.expired())
    std::cout << "expired" << std::endl;
}

Lambdas

C++11中引入了functional programming的lambda特性, 你能使用lambda当需要 function object或function或 std::function .如下例子:

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
  std::vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);

  std::for_each(std::begin(v), std::end(v),
                [](int n) {std::cout << n << std::endl;});

  auto is_odd = [](int n) {return n % 2 == 1;};
  auto pos = std::find_if(std::begin(v), std::end(v), is_odd);
  if (pos != std::end(v))
    std::cout << *pos << std::endl;
  return 0;
}

并且可以递归形式的lambda,但如果是哟功能 auto 来声明递归lambda会有如下错误:

auto fib = [&fib](int n) {return n < 2 ? 1 : fib(n-1) + fib(n-2);};
./lambdas.cc: In lambda function:
./lambdas.cc:19:55: error: ‘fib’ cannot be used as a function
./lambdas.cc:19:66: error: ‘fib’ cannot be used as a function
./lambdas.cc:19:68: error: return-statement with a value, in function returning 'void' [-fpermissive]
./lambdas.cc: In function ‘int main()’:
./lambdas.cc:19:68: error: variable ‘auto fib’ with ‘auto’ type used in its own initializer

问题是, auto 意味着对象的类型由它的初始化决定,但是初始化中却包含自己的引用,需要知道它自己的类型,那么就一个循环问题,不知道这个对象的类型了.

所以用如下指定函数的类型:

std::function<int(int)>  fib =
      [&fib](int n) {return n < 2 ? 1 : fib(n-1) + fib(n-2);};

non-member begin() and end()

C++11中提供了函数 begin()end() ,很方便的获得 STL containers的 begin和end点,而且同样适用于C的arrays.

如果不用 begin()end() ,那么对C的arrays可以如下操作:

int arr[] = {1,2,3};
std::for_each(&arr[0], &arr[0]+sizeof(arr)/sizeof(arr[0]),
              [](int n) {std::cout << n << std::endl;});

auto is_odd = [](int n) {return n%2==1;};
auto begin = &arr[0];
auto end = &arr[0]+sizeof(arr)/sizeof(arr[0]);
auto pos = std::find_if(begin, end, is_odd);
if(pos != end)
  std::cout << *pos << std::endl;

使用 begin()end():

int arr[] = {1,2,3};
std::for_each(std::begin(arr), std::end(arr),
              [](int n) {std::cout << n << std::endl;});

auto is_odd = [](int n) {return n%2==1;};
auto pos = std::find_if(std::begin(arr), std::end(arr), is_odd);
if(pos != std::end(arr))
  std::cout << *pos << std::endl;

与STL containers的操作类似,可以整合成:

#include <iostream>
#include <algorithm>

template <typename Iterator>
void bar(Iterator begin, Iterator end)  {
  std::for_each(begin, end, [](int n) {std::cout << n << std::endl;});

  auto is_odd = [](int n) {return n%2==1;};
  auto pos = std::find_if(begin, end, is_odd);
  if(pos != end)
    std::cout << *pos << std::endl;
}

template <typename C>
void foo(C c) {
  bar(std::begin(c), std::end(c));
}

template <typename T, size_t N>
void foo(T(&arr)[N]) {
  bar(std::begin(arr), std::end(arr));
}

int main() {
  int arr[] = {1,2,3};
  foo(arr);

  std::vector<int> v;
  v.push_back(1);
  v.push_back(2);
  v.push_back(3);
  foo(v);
  return 0;
}

staticassert

static_assert 可以用来在compile时进行assertion.

template <typename T, size_t Size>
class Vector {
  static_assert(Size < 3, "Size is too small");
  T _points[Size];
};

int main()
{
  Vector<int, 16> a1;
  Vector<double, 2> a2;
  return 0;
}

产生如下错误:

./static_assert.cc: In instantiation of ‘class Vector<int, 16u>’:
./static_assert.cc:12:20:   required from here
./static_assert.cc:6:4: error: static assertion failed: Size is too small

Variable Templates

Author: Shi Shougang

Created: 2015-04-12 Sun 19:52

Emacs 24.3.1 (Org mode 8.2.10)

Validate