This article describes the new features in the C++11 standards.

Table of Contents

Perf

vector<string> v;
string s("hello");
v.push_back(s); // push_back(const T&);
v.push_back(s + "world"); // push_back(T&&);
v.emplace_back("hello"); // emplace_back(Args&&...)
v.emplace_back(); // v.push_back(string());
  • Use explicit temporaries only when necessary
  • For vector, etc.:
    • Call push_back() for T lvalues/rvalues and braced-init-lists
    • Call emplace_back() for other types, 0 args, and 2+ args
      • Emplacement can invoke explicit constructors
  • new and delete are radioactive
    • Use make_shared and make_unique
  • new[] and delete[] are radioactive
    • Use vector and string
  • Manual resource management is radioactive
    • At the very least, use Boost.ScopeExit/etc
    • Ideally, write classes for automatic resource management
  • Each automatic resource manager
    • should acquire/release exactly one resource, or
    • should acquire/release multiple resources very carefully
  • Don’t return by const value
    • Inhibits move semantics, doesn’t achieve anything useful
  • Don’t move() when returning local x by value x
    • The NRVO(Named Return Value Optimization) and move semantics are designed to work together
    • NRVO applicable -> direct construction is optimal
    • NRVO inapplicable -> move semantics is efficient
  • Don’t return by rvalue reference
    • For experts only, extremely rare
    • Even the Standardization Committee got burned
    • Valid examples: forward,move,declval,get(tuple&&)
  • Always use nullptr, never use 0/NULL
    • The type is what matters, not the spelling
  • Rely on template argument deduction
    • You control its inputs - the function arguments
    • Change their types/value categories to affect the output
  • Avoid explicit template arguments, unless required
    • Required: forward<T>(t), make_shared<T>(a, b, c)
    • Wrong: make_shared<T, A, B, C>(a, b, c)
  • STL algorithms (and containers) take functors
    • function pointers, function objects, lambdas
    • NOT pointers to member functions
    • NOT operators
  • less<T> adapts operator syntax to functor syntax
  • Lambdas aren’t always better
    • sort(v.begin(), v.end(), [](const Elem& l, const Elem& r) { return l > r; });
    • sort(v.begin(), v.end(), greater<Elem>())
    • map<Key, Value, greater<Key>>
    • sometimes lambda is just too verbose
  • When to help the compiler
    • Casts (avoid when possible, never use C casts)
    • Moves (use carefully)
      • Are you certain that stealing from lvalues is safe?
      • ‘moved-from’==’unspecified state’
        • Special: moved-from shared_ptrs/unique_ptrs are empty
      • move() is friendly syntax for a value category cast
    • enable_if/SFINAE (prefer tag dispatch, static_assert)
    • Common theme: Do you have high-level knowledge?

Lambda Expression

Lambda Expression Syntax

[capture](parameters)->return-type {body}

Example:

int main() {
    char s[] = "Hello World!";
    int nuppercase = 0; // modified by the lambda
    for_each(s, s+sizeof(s), [&nuppercase] (char c) {
        if (isupper(c)) ++nuppercase;
    });
    cout << "number of uppercase char = " << nuppercase << endl;
}

[&nuppercase] means pass nuppercase by reference, otherwise pass by value.

constexpr

can be used to enable that expressions be evaluated at compile time.

std::numeric_limits<int>::max();
constexpr size_t square(int x) { return x * x; }

自动类型推导和 decltype

在 C++03 中,声明对象的同时必须指明其类型,其实大多数情况下,声明对象的同时也会包括一个初始值,C++11 在这种情况下就能够让你声明对象时不再指定类型了:

auto x=0; //0 是 int 类型,所以 x 也是 int 类型
auto c='a'; //char
auto d=0.5; //double
auto national_debt=14400000000000LL;//long long

这个特性在对象的类型很大很长的时候很有用,如:

void func(const vector<int> &vi) {
    vector<int>::const_iterator ci = vi.begin();
    // or
    const auto it = vi.begin();
}

C++11 也提供了从对象或表达式中“俘获”类型的机制,新的操作符 decltype 可以从一个表达式中“俘获”其结果的类型并“返回”:

const vector<int> vi;
typedef decltype (vi.begin()) CIT;
CIT another_const_iterator;

统一的初始化语法 uniform initialization

C++ 最少有 4 种不同的初始化形式

  1. 如括号内初始化,见:

    std::string s("hello");
    int m = int(); //default initialization
    
  2. 还有等号形式的:

    std::string s = "hello";
    int x=5;
    
  3. 对于 POD 集合,又可以用大括号:

    int arr[4] = {0,1,2,3};
    struct tm today = {0};
    
  4. 最后还有构造函数的成员初始化:

    struct S {
      int x;
      S(): x(0) {}
    };
    

这些弊端:

  • 过多的初始化方式,
  • C++03 中不能初始化 POD 数组的类成员, 也不能在使用 new[] 的时候初始 POD 数组

促成了 C++11 的统一大括号初始化方式:

struct C {
    C(int i, int j);
    int a;
    int b;
};
C c {0,0}; // C++11 only. 相当于 C c(0,0);
int* a = new int[3]{1, 2, 3}; // C++11 only
struct T {
    T() : a{1,2,3,4} {} // C++11, 初始化数组成员
    int a[4];
};
Note:
narrowing initializations
those that reduce precision or where the supplied values gets modified are not possible with braces. This is relying on the actual value of initializers floating-point to integer conversions are always considered narrowing, even 1.0 to 1
int x1(1.1), x2 = 1.2; // OK
int x3{1.3}, x4 = {1.4}; // ERROR: narrowing
char c1{7}; // ok, 7 is an int, but is not narrowing
char c2{255}; // ERROR: narrowing

还有就是对于容器而言,终于可以摆脱 push_back() 调用了,C++11 中可以直观地初始化容器了:

// C++11 container initializer
vector<string> v {"hello", "world", "from", "cpp"};
map<string, string> singers {
    {"Will", "+86 (111) 555-8888"},
    {"John", "+86 (111) 555-9999"}
};

而类中的数据成员初始化也得到了支持:

class T {
private:
    int m_size = 0; // C++11 only
public:
    T();
};

std::initializer_list<>

void echo(std::initializer_list<int> values) {
    for (auto& v : values)
        std::cout << v << ", ";
}

when there are constructors for both a specific number of arguments and an initializer_list, the version with the inirializer_list is preferred.

deleted 函数和 defaulted 函数

像以下形式的函数:

struct T {
    T()=default; // C++11
    virtual ~T()=default; // C++11
};

叫做 defaulted 函数,=default; 指示编译器生成该函数的默认实现。这有两个好处:一是少敲键盘,二是有更好的性能。 与 defaulted 函数相对的就是 deleted 函数:int func()=delete; 这货有一大用途就是实现 noncopyabe 防止对象拷贝,要想禁止拷贝,用 =deleted 声明一下两个关键的成员函数就可以了:

struct NoCopy {
    NoCopy& operator=(const NoCopy&) = delete;
    NoCopy (const NoCopy&) = delete;
};
NoCopy a;
NoCopy b(a); // 编译错误,拷贝构造函数是 deleted 函数

nullptr

nullptr 是一个新的 C++ 关键字,它是空指针常量,它是用来替代高风险的 NULL 宏和 0 字面量的。nullptr 是强类型的:<

void f(int); //#1
void f(char *);//#2
//C++03
f(0); //调用的是哪个 f?
//C++11
f(nullptr) //毫无疑问,调用的是 #2

所有跟指针有关的地方都可以用 nullptr,包括函数指针和成员指针:

const char *pc=str.c_str(); //data pointers
if (pc!=nullptr)
  cout&lt;&lt;pc&lt;&lt;endl;
int (A::*pmf)()=nullptr; //指向成员函数的指针
void (*pmf)()=nullptr; //指向函数的指针

委托构造函数

C++11 中构造函数可以调用同一个类的另一个构造函数:

class M { //C++11 delegating constructors
    int x, y;
    char *p;
public:
    M(int v) : x(v), y(0),  p(new char [MAX])  {} //#1 target
    M(): M(0) {cout << "delegating ctor" << end;} //#2 delegating

#2 就是所谓的委托构造函数,调用了真正的构造函数 #1。

右值引用

在 C++03 中的引用类型是只绑定左值的,C++11 引用一个新的引用类型叫右值引用类型,它是绑定到右值的,如临时对象或字面量。

增加右值引用的主要原因是为了实现 move 语义。与传统的拷贝不同,move 的意思是目标对象“窃取”原对象的资源,并将源置于“空”状态。当拷贝一个对象时,其实代价昂贵且无必要,move 操作就可以替代它。如在 string 交换的时候,使用 move 意义就有巨大的性能提升,如原方案是这样的:

void naive_swap(string &a, string &b) {
    string temp = a;
    a = b;
    b = temp;
}

一般而言,这种法案效率较低,因为涉及到申请内存,然后拷贝字符,而 move 就只需要交换两个数据成员,省去了申请、释放内存和拷贝字符数组等一系列操作:

要实现支持 move 的类,需要声明 move 构造函数和 move 赋值操作符,如下:

class Movable {
    Movable (Movable&&); //move constructor
    Movable&& operator=(Movable&&); //move assignment operator
};

C++11 的标准库广泛使用 move 语义,很多算法和容器都已经使用 move 语义优化过了。

C++11 的标准库

除 TR1 包含的新容器 (unordered_set, unordered_map, unordered_multiset, 和 unordered_multimap),还有一些新的库,如正则表达式,tuple,函数对象封装器等。 下面介绍一些 C++11 的标准库新特性:

线程库

从程序员的角度来看,C++11 最重要的特性就是并发了。C++11 提供了 thread 类,也提供了 promise 和 future 用以并发环境中的同步,用 async() 函数模板执行并发任务,和 thread_local 存储声明为特定线程独占的数据,这里(http://www.devx.com/SpecialReports/Article/38883)

引用自:C++11 并发指南一(C++11 多线程初探)

C++11 新标准中引入了四个头文件来支持多线程编程,他们分别是<atomic> ,<thread>,<mutex>,<condition_variable>和<future>。

  • <atomic>:该头文主要声明了两个类, std::atomic 和 std::atomic_flag,另外还声明了一套 C 风格的原子类型和与 C 兼容的原子操作的函数。
  • <thread>:该头文件主要声明了 std::thread 类,另外 std::this_thread 命名空间也在该头文件中。
  • <mutex>:该头文件主要声明了与互斥量(mutex)相关的类,包括 std::mutex 系列类,std::lock_guard, std::unique_lock, 以及其他的类型和函数。
  • <condition_variable>:该头文件主要声明了与条件变量相关的类,包括 std::condition_variable 和 std::condition_variable_any。
  • <future>:该头文件主要声明了 std::promise, std::package_task 两个 Provider 类,以及 std::future 和 std::shared_future 两个 Future 类,另外还有一些与之相关的类型和函数,std::async() 函数就声明在此头文件中。

实例代码:

void taskfunc(int idx) {
    cout << "thread " << idx << " run" << endl;
}

#define TCNT 5U // thread count

int main(int , char **) {
    vector<thread *> tv;
    for (int i = 0; i < TCNT; ++i)
        tv.push_back(new thread(taskfunc, i));
    for (auto p : tv)
        p->join();

    for (auto p : tv)
        delete p;

    return 0;
}
// 运行结果:
% ./test
thread 0 run
thread 1 run
thread 2 run
tthhrreeaadd  43  rruunn
vector<future<string>> v;
auto flip = [](string s)->string { reverse(s.begin(), s.end()); return s; };
v.push_back(async([&flip](string s){return flip(s);}, "hello"));
v.push_back(async([&flip](string s){return flip(s);}, "world"));
v.push_back(async([&flip](){return flip("c++11");}));
for (auto& f : v) cout << f.get() << endl;

互斥量
多个线程同时访问共享资源的时候需要需要用到互斥量,当一个线程锁住了互斥量后,其他线程必须等待这个互斥量解锁后才能访问它。thread提供了四种不同的互斥量:
独占式互斥量non-recursive (std::mutex)
递归式互斥量recursive (std::recursive_mutex)
允许超时的独占式互斥量non-recursive that allows timeouts on the lock functions(std::timed_mutex)
允许超时的递归式互斥量recursive mutex that allows timeouts on the lock functions (std::recursive_timed_mutex)

独占式互斥量
独占式互斥量加解锁是成对的,同一个线程内独占式互斥量在没有解锁的情况下,再次对它进行加锁这是不对的,会得到一个未定义行为。

上面的程序输出结果看起来有点乱(std::cout不是线程安全的),所以我们需要在它们访问共享资源的时候使用互斥量加锁。在线程中std::mutex使用成员函数lock加锁unlock解锁,看起来工作的很好,但这样是不安全的,你得始终记住lock之后一定要unlock,但是如果在它们中间出现了异常或者线程直接退出了unlock就没有执行,因为这个互斥量是独占式的,所以在thread1没有解锁之前,其他使用这个互斥量加锁的线程会一直处于等待状态得不到执行。lock_guard模板类使用RAII手法封装互斥量,在实例化对象的时候帮你加锁,并且能保证在离开作用域的时候自动解锁,所以你应该用lock_guard来帮你加解锁。

mutex m;

void taskfunc(int idx) {
    m.lock();
    cout << "thread " << idx << " run" << endl;
    m.unlock();
}

新的智能指针类

C++98 定义的唯一的智能指针类 auto_ptr 已经被弃用,C++11 引入了新的智能针对类 shared_ptr 和 unique_ptr。它们都是标准库的其它组件兼容,可以安全地把智能指针存入标准容器,也可以安全地用标准算法“倒腾”它们。

新的算法

主要是 all_of()、any_of() 和 none_of(),下面是例子:

#include <algorithm>
// C++11 code
//are all of the elements positive?
all_of(first, first+n, ispositive()); //false
//is there at least one positive element?
any_of(first, first+n, ispositive());//true
// are none of the elements positive?
none_of(first, first+n, ispositive()); //false

还有一个新的 copy_n:

vector<int> vi;
// read 5 integers from stdin
copy_n(istream_iterator<int>(cin), 5, back_inserter(vi));

iota() 算法可以用来创建递增序列,它先把初值赋值给 *first,然后用前置 ++ 操作符增长初值并赋值到给下一个迭代器指向的元素,如下:

#include <algorithm>
int a[5] = {0};
char c[3] = {0};
iota(a, a+5, 10); //changes a to {10,11,12,13,14}
iota(c, c+3, 'a'); //{'a','b','c'}

总结

诚然,C++11 仍缺少一些实用的库如 XML API,socket,GUI、反射——以及自动垃圾收集。然而现有特性已经让 C++ 更安全、高效(是的,效率更高了,可以参见 Google 的 基准测试结果 http://www.itproportal.com/2011/06/07/googles-rates-c-most-complex-highest-performing-language/)以及更加易于学习和使用。

如果觉得 C++ 变化太大了,不必惊恐,花点时间来学习就好了。可能在你融会贯通新特性以后,你会同意 Stroustrup 的观点:C++11 是一门新的语言——一个更好的 C++。

Tips

for-ranged loops

  • most of the time, you should use:
    • for (auto& e : r)
    • for (const auto& e : r)
  • anything else?
    • actually want a copy: for (auto e : r) // intentional copy
    • actually want a conversion: for (uint64_t n : r) // uint32_t -> 64
    • proxies might be involved: for (auto&& e : r) // binds to everything
      • Examples: vector<bool>
  • efficiency
for (const auto& p : m) // faster
for (const pair<string, int>& p : m) // may indicate constructs temporary
	// if m's value type is pair<const string, int>
	// a temporary pair<string, int> is constructed

RVO

URVO
Unnamed Return Value Optimization /all paths return rvalues
NRVO
Named Return Value Optimization /all paths return same local

Everything else: assume an extra copy

API

Pure API design overhead

string nextLine(istream&);
bool nextLine(istream&, string& s); // faster

Containers

  • Appending to containers: cheap
  • Concatenating containers: expensive
  • Returning containers by value worse than appending

Bitfields

  • Built-in bitfields
    • CANNOT get/set without a shift
    • CANNOT get/set entire store at once
    • Inefficient code (vicious circle)
  • User-defined bitfields with better primitives
Bitfields<1, 3, 4, 8> bf; // 16 bits
bf.setStore(0); // clear entire bitfield
set<1>(bf, 6); // sets second field to 6
auto x = get<2>(bf); // gets third field
// Support(I): Summation
template<unsigned...> struct Sum;
template<unsigned size>
struct Sum<size> { enum { value = size }; };
template<unsigned size, unsigned... sizes>
struct Sum<size, sizes...> {
	enum { value = size + Sum<sizes...>::value }
};
static_assert(Sum<1, 2, 3>::value == 6, "");
// Support(II): Store
template<unsigned bits> struct Store;
template<> struct Store<8> { typedef uint8_t Type; }
template<> struct Store<16> { typedef uint16_t Type; }
template<> struct Store<32> { typedef uint32_t Type; }
template<> struct Store<64> { typedef uint64_t Type; }
// Definition
template<unsigned... sizes>
class Bitfields {
  typename Store<Sum<sizes...>::value>::Type store;
public:
  template<unsigned pos, unsigned b4, unsigned size, unsigned... more>
  friend unsigned getImpl(Bitfields<size, more...>);
  ...
};
// Getting Field's Value
template<unsigned pos, unsigned... sizes>
unsigned get(Bitfields<sizes...> bf) { return getImpl<pos, 0>(bf); }
template<unsigned pos, unsigned b4, unsigned size, unsigned... sizes>
unsigned getImpl(Bitfields<size, sizes...> bf) {
  static_assert(pos <= sizeof...(sizes), "Invalid bitfield access");
  if (pos == 0) {
    if (size == 1) return (bf.store & (1u << b4)) != 0;
    return (bf.store >> b4) & ((1u << size) - 1);
  }
  return getImpl<pos - (pos ? 1 : 0), b4 + (pos ? size : 0)>(bf);
}
// More bitfield primitives
// set
// getNoShift, setNoShift
// maskAt, mask for a given bitfield
// getStore, setStore

参考

  1. http://blog.csdn.net/candy060403/article/details/7414456
  2. http://www.cnblogs.com/hujian/archive/2012/12/10/2810813.html
  3. 维基百科上关于 C++11 新特性的介绍,中文C++11介绍英文C++11介绍
  4. C++之父 Bjarne Stroustrup 的关于 C++11 的 FAQ
  5. http://www.open-std.org/jtc1/sc22/wg21/
  6. C++0x/C++11 Support in GCC:http://gcc.gnu.org/projects/cxx0x.html
  7. What is C++0x:https://www2.research.att.com/~bs/what-is-2009.pdf
  8. Overview of the New C++http://www.artima.com/shop/overview_of_the_new_cpp
  9. Overview of the New C++ (C++0x).pdf:http://ishare.iask.sina.com.cn/f/20120005.html?from=like
  10. A Brief Look at C++0xhttp://www.artima.com/cppsource/cpp0x.html
  11. Summary of C++11 Feature Availability in gcc and MSVC:http://www.aristeia.com/C++11/C++11FeatureAvailability.htm
  12. C++ 11: Come Closer:http://www.codeproject.com/Articles/344282/Cplusplus-11-Come-Closer
  13. C++11 threads, locks and condition variables: http://www.codeproject.com/Articles/598695/Cplusplus11-threads-locks-and-condition-variables
  14. Move Semantics and Perfect Forwarding in C++11:http://www.codeproject.com/Articles/397492/Move-Semantics-and-Perfect-Forwarding-in-Cplusplus
  15. http://solarianprogrammer.com/categories/C++11/
  16. C++11 Concurrency:http://www.baptiste-wicht.com/2012/03/cpp11-concurrency-part1-start-threads/
  17. http://www.hpl.hp.com/personal/Hans_Boehm/misc_slides/sfacm-cleaned.pdf
  18. http://en.cppreference.com/w/cpp/thread
  19. http://isocpp.org/blog/2012/12/c11-a-cheat-sheet-alex-sinyakov
  20. The Biggest Changes in C++11:http://blog.smartbear.com/c-plus-plus/the-biggest-changes-in-c11-and-why-you-should-care/
  21. Ten C++11 Features Every C++ Developer Should Use:http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer
  22.  C++11 – A Glance [part 1 of n]:http://www.codeproject.com/Articles/312029/Cplusplus11-A-Glance-part-1-of-n
  23.  C++11 – A Glance [part 2 of n]:http://www.codeproject.com/Articles/314415/Cplusplus11-A-Glance-part-2-of-n
  24. C++11(及现代C++风格)和快速迭代式开发:http://mindhacks.cn/2012/08/27/modern-cpp-practices/
  25. Lambda Functions in C++11 - the Definitive Guide:http://www.cprogramming.com/c++11/c++11-lambda-closures.html
  26. Better types in C++11 - nullptr, enum classes (strongly typed enumerations) and cstdint:http://www.cprogramming.com/c++11/c++11-nullptr-strongly-typed-enum-class.html
  27. Rvalue-references-and-move-semantics-in-c++11:http://www.cprogramming.com/c++11/rvalue-references-and-move-semantics-in-c++11.html
  28. http://www.gotw.ca/publications/index.htm
  29. http://www.devx.com/SpecialReports/Door/38865
  30. Multi-threading in C++0x:http://accu.org/index.php/journals/1584
  31. C++ 0X feature summary cheat sheat:http://www.iesensor.com/blog/2011/05/31/c-0x-feature-summary-cheat-sheat/
  32. Multithreading in C++0x part 1: Starting Threads:http://www.justsoftwaresolutions.co.uk/threading/multithreading-in-c++0x-part-1-starting-threads.html
  33. http://en.cppreference.com/w/cpp/thread
  34. http://www.cplusplus.com/reference/multithreading/