跟Google学写代码--Chromium工程中用到的C++11特性

Ttile 跟Google学写代码--Chromium工程中用到的C++11特性

Chromium是一个伟大的、庞大的开源工程,很多值得我们学习的地方。

《跟Google学写代码–Chromium/base–stl_util源码学习及应用》

《跟Google学写代码–Chromium/base–windows_version源码学习及应用》

《跟Google学写代码–Chromium/base–cpu源码学习及应用》

今天就与大家一起分享一下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
}

原文发布于微信公众号 - 程序员的酒和故事(cppdabaojian)

原文发表时间:2017-02-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏决胜机器学习

有趣的算法(三)——Hash算法

有趣的算法(三)——Hash算法 (原创内容,转载请注明来源,谢谢) 一、Hash算法 近期看到用hash实现基于hash的简单的小型数据库(传统大型数据...

38270
来自专栏一个爱吃西瓜的程序员

每天学习一点儿算法--广度优先搜索

广度优先搜索(BFS)是我们学的第一种图算法,它可以让你找出两样东西之间的最短距离。 这里提到了一个新的概念:图, 那什么是图呢? 图简介 图用于模拟不同的东...

33840
来自专栏软件开发 -- 分享 互助 成长

原型模式C++类的复制构造函数和赋值运算符

一、简介 1、原型模式,用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 2、为什么会用到原型模式? (1)既然可以直接new,为什么会用到原型...

27050
来自专栏mySoul

设计模式-原型模式

关于Cloneable 接口,用途和Serializable一样为标记型接口,内部没有方法和属性,implements Cloneable 表示对象能被克隆,即...

10410
来自专栏企鹅号快讯

R包系列——stringr包

stringr包是Hadley Wickham大神贡献的R包之一,主要用于字符串的处理。对于经常需要对数据进行预处理的分析人员来说,简直是一把“利器”,可谓是上...

35160
来自专栏大史住在大前端

野生前端的数据结构基础练习(2)——队列

循环队列书中并没有提及,它是一种特殊的队列。简单理解就是将基本队列只当做存储结构,而使用front和rear两个指针分别代表队列的头和尾,实际对外表现的队列是f...

22430
来自专栏Lambda

Java8新日期处理API

Java8引入了一套全新的时间日期API,本篇随笔将说明学习java8的这套API。 java.time包中的是类是不可变且线程安全的。新的时间及日期API位...

516100
来自专栏星流全栈

ES5和ES6中的继承

10230
来自专栏逆向技术

win32编程简介

  我们要编写windos程序.都离不开API. 也就是我们所说的win32程序. 所以学好win32是你能不能再windows下编写程序的基础.

31530
来自专栏Vamei实验室

纸上谈兵: 哈希表 (hash table)

HASH 哈希表(hash table)是从一个集合A到另一个集合B的映射(mapping)。映射是一种对应关系,而且集合A的某个元素只能对应集合B中的一个元素...

229100

扫码关注云+社区

领取腾讯云代金券