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

相关文章

来自专栏数据科学学习手札

(数据科学学习手札42)folium进阶内容介绍

  在上一篇(数据科学学习手札41)中我们了解了folium的基础内容,实际上folium在地理信息可视化上的真正过人之处在于其绘制图像的高度可定制化上,本文就...

2154
来自专栏移动端开发

Swift 基础部分(建议掌握OC字符串知识的翻阅)

更新说明:         Swift 目前已经发布到4.0版本了,以前写的这整个Swift学习系列的文章,有很多的不足之处,我会重新整理整个系列文章,也是相...

1789
来自专栏前端架构与工程

【翻译】JavaScript中5个值得被广泛使用的数组方法

原文地址:http://colintoh.com/blog/5-array-methods-that-you-should-use-today?utm_sour...

2007
来自专栏HTML5学堂

2015.12.21 HTML5真题练习

HTML5学堂:每天一道题,强壮程序员!今日主要涉及12.18日关于字符串相关知识题目的解答,以及一道涉及数据类型的题目。 HTML5真题【2015.12.18...

2777
来自专栏吾爱乐享

Java练习之–面向对象

923
来自专栏偏前端工程师的驿站

ES6 Features系列:Template Strings & Tagged Template Strings

1. Brief   ES6(ECMAScript 6th edition)于2015年7月份发布,虽然各大浏览器仍未全面支持ES6,但我们可以在后端通过Nod...

1717
来自专栏抠抠空间

JSON和Django内置序列化

JSON 什么是JSON JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation) JSON 是轻量级的文本数...

3256
来自专栏数据小魔方

Xcelsius(水晶易表)系列8——动态选择器高级用法

今天继续跟大家分享关于水晶易表的动态选择器高级用法。 该案例所用到的函数和选择器工具还是我们之前用到的基本选择工具——单选按钮,组合框。 用到的思想与昨天讲到的...

2676
来自专栏程序员的SOD蜜

JavaScript的“原型甘露”

今天跟朋友讨论JS的面向对象编程问题,想起了原来曾经看过一篇文章,但是看过很久想不起来了,用了很多关键词,终于用“悟透JavaScript  面向对象”这两个关...

2268
来自专栏数据小魔方

查询与引用函数——offset函数

今天要跟大家分享的是一个强大的查询与引用函数——offset函数! OFFSET函数的语法参数相对比较多,但是因为参数位置和函数都比较固定,所以只要能够理解各自...

3077

扫描关注云+社区