谷歌出品,必属精品——Chromium工程中C++11特性

谷歌出品,必属精品——Chromium工程中C++11特性Chromium也许是最牛的C++开源库,里面用到了很多C++11的知识,这里就跟大家分享一下Chromium工程中使用了哪些C++11,禁用了哪些C++11特性。

谷歌出品,必属精品——Chromium工程中C++11特性

Chromium中使用的C++11特性

__func__

Within the function body, the function-local predefined variable __func__is defined as if by:

static const char __func__[] = "function-name";

使用代码:

#include<iostream>

void foo()

{

std::cout << __func__ << std::endl;

return;

}

int main(int argc, char* argv[]) {

foo();

std::cout << __func__ << std::endl;

system("pause");

return 0;

}

//输出:

//foo

//main

>> 代替 > >, <:: 代替 < ::

C++11之前的写法:

vector<vector<float> > MyMatrix;

C++11的写法:

vector<vector<float>> MyMatrix;

std::array

使用std::array来替代常规数组,std::array是定长的,但是支持stl算法:

#include <string>

#include <iterator>

#include <iostream>

#include <algorithm>

#include <array>

int main()

{

// construction uses aggregate initialization

std::array<int, 3> a1{ {1, 2, 3} }; // double-braces required in C++11 (not in C++14)

std::array<int, 3> a2 = {1, 2, 3}; // never required after =

std::array<std::string, 2> a3 = { std::string("a"), "b" };

// container operations are supported

std::sort(a1.begin(), a1.end());

std::reverse_copy(a2.begin(), a2.end(), std::ostream_iterator<int>(std::cout, " "));

std::cout << '\n';

// ranged for loop is supported

for(const auto& s: a3)

std::cout << s << ' ';

}

auto

c++11新增了auto关键字:

Use auto to avoid type names that are noisy, obvious, or unimportant

#include <iostream>

#include <cmath>

#include <typeinfo>

template<class T, class U>

auto add(T t, U u) -> decltype(t + u) // the return type is the type of operator+(T, U)

{

return t + u;

}

auto get_fun(int arg) -> double (*)(double) // same as: double (*get_fun(int))(double)

{

switch (arg)

{

case 1: return std::fabs;

case 2: return std::sin;

default: return std::cos;

}

}

int main()

{

auto a = 1 + 2;

std::cout << "type of a: " << typeid(a).name() << '\n';

auto b = add(1, 1.2);

std::cout << "type of b: " << typeid(b).name() << '\n';

auto c = {1, 2};

std::cout << "type of c: " << typeid(c).name() << '\n';

auto my_lambda = [](int x) { return x + 3; };

std::cout << "my_lambda: " << my_lambda(5) << '\n';

auto my_fun = get_fun(2);

std::cout << "type of my_fun: " << typeid(my_fun).name() << '\n';

std::cout << "my_fun: " << my_fun(3) << '\n';

// auto int x; // error as of C++11: "auto" is no longer a storage-class specifier

}

constexpr

In C++11, use constexpr to define true constants or to ensure constant initialization.

但是你会问,这特么到底跟const有什么区别呢?

const applies for variables, and prevents them from being modified in your code.

constexpr tells the compiler that this expression results in a compile time constant value, so it can be used in places like array lengths, assigning to const variables, etc. The link given by Oli has a lot of excellent examples.

看几个代码片段:

int nonconst_var = 100;

const int const_var1 = 2;

const int const_var2 = nonconst_var;

constexpr int constexpr_var1 = 3 + const_var1 * 4; //成立

constexpr int constexpr_var2 = 3 + nonconst_var * 4; //错误

constexpr int constexpr_var3 = 3 + const_var2 * 4; //错误

#include <iostream>

#include <array>

using namespace std;

constexpr int foo(int i)

{

return i + 5;

}

int main()

{

int i = 10;

std::array<int, foo(5)> arr; // OK

foo(i); // Call is Ok

// But...

std::array<int, foo(i)> arr1; // Error

}

template<int N>

class list

{ };

constexpr int sqr1(int arg)

{ return arg * arg; }

int sqr2(int arg)

{ return arg * arg; }

int main()

{

const int X = 2;

list<sqr1(X)> mylist1; // OK: sqr1 is constexpr

list<sqr2(X)> mylist2; // wrong: sqr2 is not constexpr

return 0;

}

decltype

个人觉得,最重要的用途就是在模板中:

template<typename T, typename U>

auto add(T t, U u) -> decltype(t + u); // return type depends on template parameters

decltype(a->x) y; // type of y is double (declared type)

scoped_ptr& operator=(decltype(nullptr)) {

reset();

return *this;

}

Function(arguments) = default

explicitly-defaulted function definition: as an explicit instruction to the compiler to generate special member function for a class.

我们声明了类的构造函数,那么编译器就不再给我们提供默认的构造函数了。

如果我们既想要自己声明的构造函数,又要编译器提供给我们的默认构造函数,那么default关键字就派上用场了:

class X {

// ...

X& operator=(const X&) = delete; // Disallow copying

X(const X&) = delete;

};

template <typename T = type> type Function(T var) {}

模板的默认参数:

template <typename T = int>

void DefTempParm() {}

这个特性在C++11之前是不支持的。

Class() : Class(0) {}Class(type var) : Class(var, 0) {}

这个叫Delegated Constructors,委托构造函数或是委派构造函数。

在介绍Delegated Constructors之前,我们看一段代码:

class A{

public:

A(): num1(0), num2(0) {average=(num1+num2)/2;}

A(int i): num1(i), num2(0) {average=(num1+num2)/2;}

A(int i, int j): num1(i), num2(j) {average=(num1+num2)/2;}

private:

int num1;

int num2;

int average;

};

有三个构造函数,构造函数的函数体是一致,一看就是代码非常的冗余。

你可以使用一个函数进行包装:

public:

A(): num1(0), num2(0) {init();}

A(int i): num1(i), num2(0) {init();}

A(int i, int j): num1(i), num2(j) {init();}

private:

int num1;

int num2;

int average;

void init(){ average=(num1+num2)/2;};

};

但是,如果我们不小心,在其他地方调用了init怎么办呢?

基于以上类成员构造所面临的问题,C++11 标准提出了委派构造函数新特性。利用这个特性,程序员可以将公有的类成员构造代码集中在某一个构造函数里,这个函数被称为目标构造函数。其他构造函数通过调用目标构造函数来实现类成员构造,这些构造函数被称为委派构造函数。

#include <iostream>

using namespace std;

class A{

public:

A(): A(0){ cout << "In A()" << endl;}

A(int i): A(i, 0){cout << "In A(int i)" << endl;}

A(int i, int j){

num1=i;

num2=j;

average=(num1+num2)/2;

cout << "In A(int i, int j)" << endl;

}

private:

int num1;

int num2;

int average;

};

int main(){

A a;

return 0;

}

可以看到,在构造函数 A()的初始化列表里,程序调用了 A(0), 这就是委派构造函数的语法。 我们称 A(int i)为 A()的目标构造函数,而 A()为 A(int i)的委派构造函数。同理,A(int i, int j)为 A(int i)的目标构造函数,而 A(int i) 为 A(int i, int j)的委派构造函数。在利用了委派构造函数后,整个程序变得更加的清楚和简洁。目标构造函数和委派构造函数跟其他普通的构造函数一样有相同的接口和语法,它们并没有特殊的处理和标签。从这个例子还可以看到,一个委派构造函数可以是另一个委派构造函数的目标构造函数,委派构造函数和目标构造函数是相对而言的。目标构造函数是通过重载和类参数推导准则而选定的。

final

Indicates that a class or function is final and cannot be overridden

这里不要误解了,不只是函数,类也可以声明为final:

struct Base

{

virtual void foo();

};

struct A : Base

{

void foo() final; // A::foo is overridden and it is the final override

void bar() final; // Error: non-virtual function cannot be overridden or be final

};

struct B final : A // struct B is final

{

void foo() override; // Error: foo cannot be overridden as it's final in A

};

struct C : B // Error: B is final

{

};

Function(arguments) = delete;

Suppresses the implementation of a function

比如,在单利中,我们往往是把类的赋值构造函数和operator=函数设置为delete:

#define SINGLETON_DEFINE(TypeName) \

static TypeName* GetInstance() \

{ \

static TypeName type_instance; \

return &type_instance; \

} \

\

TypeName(const TypeName&) = delete; \

TypeName& operator=(const TypeName&) = delete

}

class Derived : Base { using Base::Base; };

struct B1 { B1(int); };

struct B2 { B2(int); };

struct D2 : B1, B2 {

using B1::B1;

using B2::B2;

D2(int); // OK: D2::D2(int) hides both B1::B1(int) and B2::B2(int)

};

D2 d2(0); // calls D2::D2(int)

[captures](params) -> ret { body }

C++11有了lambda表达式,这里就不过多介绍了:

std::sort(v.begin(), v.end(), [](int x, int y) {

return Weight(x) < Weight(y);

});

noexcept

Specifies that a function will not throw exceptions

void f() noexcept; // the function f() does not throw

void (*fp)() noexcept(false); // fp points to a function that may throw

void g(void pfa() noexcept); // g takes a pointer to function that doesn't throw

Non-Static Class Member Initializers

代码一目了然:

class S

{

int n; // non-static data member

int& r; // non-static data member of reference type

int a[10] = {1, 2}; // non-static data member with initializer (C++11)

std::string s, *ps; // two non-static data members

struct NestedS {

std::string s;

} d5, *d6; // two non-static data members of nested type

char bit : 2; // two-bit bitfield

};

nullptr

int* ptr = new int(2);

delete ptr;

ptr = nullptr;

override

Indicates that a class or function overrides a base implementation

void OnTraceLogEnabled() override;

void OnTraceLogDisabled() override;

for (type var : range)

基于范围的for循环,也就是其他语言的foreach:

#include <iostream>

#include <vector>

int main() {

std::vector<int> v = {0, 1, 2, 3, 4, 5};

for (const int& i : v) // access by const reference

std::cout << i << ' ';

std::cout << '\n';

for (auto i : v) // access by value, the type of i is int

std::cout << i << ' ';

std::cout << '\n';

for (auto&& i : v) // access by reference, the type of i is int&

std::cout << i << ' ';

std::cout << '\n';

for (int n : {0, 1, 2, 3, 4, 5}) // the initializer may be a braced-init-list

std::cout << n << ' ';

std::cout << '\n';

int a[] = {0, 1, 2, 3, 4, 5};

for (int n : a) // the initializer may be an array

std::cout << n << ' ';

std::cout << '\n';

for (int n : a)

std::cout << 1 << ' '; // the loop variable need not be used

std::cout << '\n';

}

Standard Integers

Provides fixed-size integers independent of platforms

static_assert(bool, string)

#include <type_traits>

template <class T>

void swap(T& a, T& b)

{

static_assert(std::is_copy_constructible<T>::value,

"Swap requires copying");

static_assert(std::is_nothrow_move_constructible<T>::value

&& std::is_nothrow_move_assignable<T>::value,

"Swap may throw");

auto c = b;

b = a;

a = c;

}

template <class T>

struct data_structure

{

static_assert(std::is_default_constructible<T>::value,

"Data Structure requires default-constructible elements");

};

struct no_copy

{

no_copy ( const no_copy& ) = delete;

no_copy () = default;

};

struct no_default

{

no_default () = delete;

};

int main()

{

int a, b;

swap(a, b);

no_copy nc_a, nc_b;

swap(nc_a, nc_b); // 1

data_structure<int> ds_ok;

data_structure<no_default> ds_error; // 2

}

Enumerated Type Classes and Enum Bases

Provide enums as full classes, with no implicit conversion to booleans or integers. Provide an explicit underlying type for enum classes and regular enums.

namespace ScopedEnumConversions

{

enum class Suit { Diamonds, Hearts, Clubs, Spades };

void AttemptConversions()

{

Suit hand;

hand = Clubs; // error C2065: 'Clubs' : undeclared identifier

hand = Suit::Clubs; //Correct.

int account_num = 135692;

hand = account_num; // error C2440: '=' : cannot convert from 'int' to 'Suit'

hand = static_cast<Suit>(account_num); // OK, but probably a bug!!!

account_num = Suit::Hearts; // error C2440: '=' : cannot convert from 'Suit' to 'int'

account_num = static_cast<int>(Suit::Hearts); // OK

}

std::vector data

vector中的data方法:

Returns pointer to the underlying array serving as element storage. The pointer is such that range [data(); data() + size()) is always a valid range, even if the container is empty (data() is not dereferenceable in that case).

使用:

#include <iostream>

#include <vector>

int main ()

{

std::vector<int> myvector (5);

int* p = myvector.data();

*p = 10;

++p;

*p = 20;

p[2] = 100;

std::cout << "myvector contains:";

for (unsigned i=0; i<myvector.size(); ++i)

std::cout << ' ' << myvector[i];

std::cout << '\n';

return 0;

}

Algorithms

使用了很多algorithm中C++11新增的算法:

bool all_of(Iter first, Iter last, Pred pred);

bool any_of(Iter first, Iter last, Pred pred);

bool none_of(Iter first, Iter last, Pred pred);

Iter find_if_not(Iter first, Iter last, Pred pred);

OutIter copy_if(InIter first, InIter last, OutIter result, Pred pred);

OutIter copy_n(InIter first, InIter::difference_type n, OutIter result);

OutIter move(InIter first, InIter last, OutIter result);

OutIter move_backward(InIter first, InIter last, OutIter result);

pair<OutIter1, OutIter2> partition_copy(InIter first, InIter last, OutIter1 out_true, OutIter2 out_false, Pred pred);

Iter partition_point(Iter first, Iter last, Pred pred);

RAIter partial_sort_copy(InIter first, InIter last, RAIter result_first, RAIter result_last);

RAIter partial_sort_copy(InIter first, InIter last, RAIter result_first, RAIter result_last, Compare comp);

bool is_sorted(Iter first, Iter last);

bool is_sorted(Iter first, Iter last, Compare comp);

Iter is_sorted_until(Iter first, Iter last);

Iter is_sorted_until(Iter first, Iter last, Compare comp);

bool is_heap(Iter first, Iter last);

bool is_heap(Iter first, Iter last, Compare comp);

Iter is_heap_until(Iter first, Iter last);

Iter is_heap_until(Iter first, Iter last, Compare comp);

T min(initializer_list<T> t);

T min(initializer_list<T> t, Compare comp);

T max(initializer_list<T> t);

T max(initializer_list<T> t, Compare comp);

pair<const T&, const T&> minmax(const T& a, const T& b);

pair<const T&, const T&> minmax(const T& a, const T& b, Compare comp);

pair<const T&, const T&> minmax(initializer_list<T> t);

pair<const T&, const T&> minmax(initializer_list<T> t, Compare comp);

pair<Iter, Iter> minmax_element(Iter first, Iter last);

pair<Iter, Iter> minmax_element(Iter first, Iter last, Compare comp);

void iota(Iter first, Iter last, T value); // For each element referred to by the iterator i in the range [first,last), assigns *i = value and increments value as if by ++value

std::begin(), std::end()

Returns an iterator to the end (i.e. the element after the last element) of the given container c or array array. These templates rely on C::end() having a reasonable implementation.

使用:

#include <iostream>

#include <vector>

#include <iterator>

#include <algorithm>

int main()

{

std::vector<int> v = { 3, 1, 4 };

if (std::find(std::begin(v), std::end(v), 5) != std::end(v)) {

std::cout << "found a 5 in vector v!\n";

}

int a[] = { 5, 10, 15 };

if (std::find(std::begin(a), std::end(a), 5) != std::end(a)) {

std::cout << "found a 5 in array a!\n";

}

}

std::enable_if, std::conditional

enable_if 的主要作用就是当某个 condition 成立时,enable_if可以提供某种类型

#include <iostream>

#include <type_traits>

// 1. the return type (bool) is only valid if T is an integral type:

template <class T>

typename std::enable_if<std::is_integral<T>::value,bool>::type

is_odd (T i) {return bool(i%2);}

// 2. the second template argument is only valid if T is an integral type:

template < class T,

class = typename std::enable_if<std::is_integral<T>::value>::type>

bool is_even (T i) {return !bool(i%2);}

int main() {

short int i = 1; // code does not compile if type of i is not integral

std::cout << std::boolalpha;

std::cout << "i is odd: " << is_odd(i) << std::endl;

std::cout << "i is even: " << is_even(i) << std::endl;

return 0;

}

std::vector::cbegin(), std::vector::cend()

在迭代器中加入了const。

std::declval<class>()

Converts any type T to a reference type, making it possible to use member functions in decltype expressions without the need to go through constructors

declval的强大之处在于它能获取任何类型的右值引用,而不管它是不是有默认构造函数:

#include <utility>

#include <iostream>

struct Default { int foo() const { return 1; } };

struct NonDefault

{

NonDefault(const NonDefault&) { }

int foo() const { return 1; }

};

int main()

{

decltype(Default().foo()) n1 = 1; // type of n1 is int

// decltype(NonDefault().foo()) n2 = n1; // error: no default constructor

decltype(std::declval<NonDefault>().foo()) n2 = n1; // type of n2 is int

std::cout << "n1 = " << n1 << '\n'

<< "n2 = " << n2 << '\n';

}

emplace(), emplace_back(), emplace_front(), emplace_hint()

之前也有博客写过关于push_back和emplace_back的区别,直接上一段代码吧,以vector为例子:

#include <vector>

#include <string>

#include <iostream>

struct President

{

std::string name;

std::string country;

int year;

President(std::string p_name, std::string p_country, int p_year)

: name(std::move(p_name)), country(std::move(p_country)), year(p_year)

{

std::cout << "I am being constructed.\n";

}

President(President&& other)

: name(std::move(other.name)), country(std::move(other.country)), year(other.year)

{

std::cout << "I am being moved.\n";

}

President& operator=(const President& other) = default;

};

int main()

{

std::vector<President> elections;

std::cout << "emplace_back:\n";

elections.emplace_back("Nelson Mandela", "South Africa", 1994);

std::vector<President> reElections;

std::cout << "\npush_back:\n";

reElections.push_back(President("Franklin Delano Roosevelt", "the USA", 1936));

std::cout << "\nContents:\n";

for (President const& president: elections) {

std::cout << president.name << " was elected president of "

<< president.country << " in " << president.year << ".\n";

}

for (President const& president: reElections) {

std::cout << president.name << " was re-elected president of "

<< president.country << " in " << president.year << ".\n";

}

}

输出结果:

emplace_back:

I am being constructed.

push_back:

I am being constructed.

I am being moved.

Contents:

Nelson Mandela was elected president of South Africa in 1994.

Franklin Delano Roosevelt was re-elected president of the USA in 1936.

std::initializer_list<T>

初始化列表:

#include <iostream>

#include <vector>

#include <initializer_list>

template <class T>

struct S {

std::vector<T> v;

S(std::initializer_list<T> l) : v(l) {

std::cout << "constructed with a " << l.size() << "-element list\n";

}

void append(std::initializer_list<T> l) {

v.insert(v.end(), l.begin(), l.end());

}

std::pair<const T*, std::size_t> c_arr() const {

return {&v[0], v.size()}; // copy list-initialization in return statement

// this is NOT a use of std::initializer_list

}

};

template <typename T>

void templated_fn(T) {}

int main()

{

S<int> s = {1, 2, 3, 4, 5}; // copy list-initialization

s.append({6, 7, 8}); // list-initialization in function call

std::cout << "The vector size is now " << s.c_arr().second << " ints:\n";

for (auto n : s.v)

std::cout << n << ' ';

std::cout << '\n';

std::cout << "Range-for over brace-init-list: \n";

for (int x : {-1, -2, -3}) // the rule for auto makes this ranged-for work

std::cout << x << ' ';

std::cout << '\n';

auto al = {10, 11, 12}; // special rule for auto

std::cout << "The list bound to auto has size() = " << al.size() << '\n';

// templated_fn({1, 2, 3}); // compiler error! "{1, 2, 3}" is not an expression,

// it has no type, and so T cannot be deduced

templated_fn<std::initializer_list<int>>({1, 2, 3}); // OK

templated_fn<std::vector<int>>({1, 2, 3}); // also OK

}

string var=R"(raw_string)";

字面量,就像Python中的原始字符串。

<tuple>

元组,曾经令多少C++程序员羡慕的东西。

#include <iostream>

#include <tuple>

using namespace std;

int main() {

tuple<int, string, double> firstRecord(42, "myFirstName", 1.303);

tuple<int, string, double> secondRecord(21, "mySecondName", 2.638);

auto concat = tuple_cat(firstRecord, secondRecord);

/* for printing a tuple of any size you have to recursively use templates */

cout << get<1>(concat) << " " << get<4>(concat) << endl;

const int a = 1;

const int b = 2;

const double x = 2.34;

const double y = 4.27;

/* tie creates a tuple from lvalue references */

if(tie(a, x) < tie(b, y))

cout << "second arguments are larger" << endl;

/*

* use this to easily implement e.g.

* bool operator<(const A& rhs) const { return tie(n, s, d) < tie(rhs.n, rhs.s, rhs.d); }

*/

int f = 1;

int g = 2;

int h = 3;

/* unpack variables */

tie(f, g, h) = tie(g, f, h);

cout << "f " << f << ", g " << g << ", h " << h << endl;

/*

* f=2, g=2, h=3, because it is evaluated as follows:

* f = g; -> f=2

* g = f; -> g=2

* h = h; -> h=3

*/

}

std::unique_ptr<type>

智能指针,这里就不过多介绍了。

Chromium中禁用的C++11特性

谷歌出品,必属精品——Chromium工程中C++11特性

Inline Namespaces

The inline namespace mechanism is intended to support library evolution by providing a mechanism that support a form of versioning

通过一段代码,你就明白了什么是inline namespace:

#include<iostream>

namespace X

{

inline namespace Y

{

void func()

{

std::cout << "func called" << std::endl;

}

}

}

namespace X2

{

namespace Y2

{

void func2()

{

std::cout << "func2 called" << std::endl;

}

}

}

int main()

{

X::func();

X::Y::func();

X2::Y2::func2();

//X2::func2(); //it is wrong

system("pause");

return 0;

}

输出:

func called

func called

func2 called

有了inline namespace,确实容易造成一些混淆,迷惑我们的双眼,导致调用错误。

ong long Type

为了存储更大的数字,我们想到了使用long long来表示,但是这是google c++ code style所禁止的,建议我们使用stdint.h。

Using

<stdint.h>

(or making your own surrogate) is very common in embedded development. It makes variable sizes and memory usage more obvious and porting a lot easier.

Date and time utilities

要使用base类中的关于time的类,不要再使用

<chrono>

关于base库中的Time和Timer接下来会跟大家一起分析。

Function Objects

要使用base库中的base::Callback。

关于base/callback接下来也会跟大家一起分享。

Regular Expressions

C++11终于在语言层面上对正则表达式进行了支持,但是chromium工程中不允许使用的。

因为使用了re2这个库:

RE2是一个下效准绳性的正则表达式库 ,由 Rob Pike 战 Russ Cox 两位去自谷歌的大牛用 C 完成。他俩同时也是 Go 语言的主导者。

Thread Library

c++11引入了thread,但是在chromium工程中,我们还是不能使用,因为已经使用了base库中的线程:

base::Thread is tightly coupled to MessageLoop which would make it hard to replace. We should investigate using standard mutexes, or unique_lock, etc. to replace our locking/synchronization classes.

std::shared_ptr

c++11引入了智能指针,这是一个千呼万唤始出来的结果。

但是对于shared_ptr会有很多的坑儿,所以不建议使用。

If dynamic allocation is necessary, prefer to keep ownership with the code that allocated it. If other code needs access to the object, consider passing it a copy, or passing a pointer or reference without transferring ownership. Prefer to use std::unique_ptr to make ownership transfer explicit. For example:

std::unique_ptr<Foo> FooFactory();

Do not design your code to use shared ownership without a very good reason.

相关推荐