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

时间:2022-05-04
本文章向大家介绍跟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
}