跟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 条评论
登录 后参与评论

相关文章

来自专栏个人随笔

Java 接口理论篇

接口的两层含义:接口作为数据类型   狭义的接口  接口的第二层含义:工具 广义的接口 在接口中所有的变量都是静态常量 在接口中所有方法都是抽象方法 定义一个接...

3126
来自专栏从零开始学自动化测试

python接口自动化21-规范的API接口文档示例

前言 接口文档到底长啥样?做接口测试最大的障碍在于没有接口文档,很多公司不注重接口文档的编写,导致测试小伙伴没见过接口文档。 运气好一点的测试小伙伴可能厚着脸皮...

8108
来自专栏撸码那些事

【封装那些事】 泄露的封装

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

javascript基础修炼(2)——What's this(上)

this是javascript关键字之一,是javascript能够实现面向对象编程的核心概念。用得好能让代码优雅高端,风骚飘逸,用不好也绝对是坑人坑己利器。我...

621
来自专栏彭湖湾的编程世界

【javascript】详解javascript闭包 — 大家准备好瓜子,我要开始讲故事啦~~

前言: 在这篇文章里,我将对那些在各种有关闭包的资料中频繁出现,但却又千篇一律,且暧昧模糊得让人难以理解的表述,做一次自己的解读。或者说是对“红宝书”的《函数表...

1906
来自专栏程序员的诗和远方

谈谈 JavaScript 中的 TDZ

本来过年期间想写这个的,不过要准备些东西,一直没抽出时间,刚好今天有点空闲。上个月阮一峰阮老师在微博上发布了这样一条信息 于是评论区炸开了锅,很多人留言指出,这...

2907
来自专栏Java帮帮-微信公众号-技术文章全总结

Java基础-06.总结二维数组,面向对象

1:二维数组(理解) (1)元素是一维数组的数组。 (2)格式: A:数据类型[][] 数组名 = new 数据类型[m][n]; B:数据类型[][]...

2514
来自专栏前端达人

用 React 构建可复用的设计系统

React 让 web 开发简化了很多。原则上 React 基于组件的模式让代码分解和复用变得更加容易。 然而,开发者并不总是清楚如何跨项目分享他们的组件。在这...

603
来自专栏aCloudDeveloper

漫谈递归转非递归

一:递归的思想       之前面试腾讯,面试官问了一个问题:说说递归和循环的区别?当时没有答出问题的本质,只是简单地解释了这两个词的意思,囧,今天就借由这篇文...

2187
来自专栏小红豆的数据分析

小蛇学python(19)装饰器

python的装饰器是python的特色高级功能之一,言简意赅得说,其作用是在不改变其原有函数和类的定义的基础上,给他们增添新的功能。

822

扫码关注云+社区