【C++】 3_C++11 新特性-语言篇

c/c++

浏览数:131

2020-6-15

AD:资源代下载服务

using 关键字

  • using 主要使用情形

using-directive for
namespace and using-declarations for
namespace members

(名称空间使用指令和名称空间成员使用声明)

using namespace std;

using std::count;

using-declarations for
class members

(类成员的使用声明)

<stl_bvector.h>

protected:
    using _Base::M_allocate;
    using _Base::M_deallocate;
    using _Base::_S_nword;
    using _Base::M_get_Bit_allocator;

<std_list.h>
    using _Base::_M_impl;
    using _Base::_M_put_node;
    using _Base::_M_get_node;
    using _Base::_M_get_Tp_allocator;
    using _Base::_M_get_Node_allocator;

noexcept 关键字

void foo() noexcpet; 

==>

void foo() noexcept(true);

declares that foo()
won't throw. If an exception is not handled local inside foo() —— thus, if foo() throws —— the program is terminated, calling std::terminate(), which by default calls std::abort().

( 声明foo()不会抛出。 如果没有在foo()内局部处理异常 —— 那么如果foo()抛出 —— 程序终止,调用std :: terminate(),默认情况下调用std :: abort()。)

You can even
specify a condition under a function throws no exception. For example, for any type Type, the global swap() usually is defined as follows:

(甚至可以在函数不引发异常的情况下指定条件。 例如,对于任何类型Type,通常将全局swap() 定义如下:)

void swap(Type &x, Type &y) noexcept(noexcept(x.swap(y)))
{
    x.swap(y);
}

Here, inside noexception (…), you can specify
a Boolean condition under which no exception gets thrown: Specifying
noexcept without noexception is a short form of specifying noexcept(true).

(在这里,在no exception(…)中,可以指定一个布尔条件,在该条件下不会引发异常:指定noexcept而不指定noexception是指定noexcept(true)的缩写形式。)

You need to inform C++(specifically std::vector) that
your move constructor and destructor does not throw. Then the move construct will be called when the vector grows.
If the construtor is not noexcept , std::vector can't use it, since then it can’t ensure the expction guarantes demanded by the standard.

(你需要通知C ++(特别是std :: vector)你的move构造函数和析构函数不会抛出。 然后,当向量增长(扩容)时,将调用move构造函数。 如果构造函数不是noexcept,则std :: vector不能使用它,因为那样就不能确保标准要求的保护性保证。)

class MyString
{
private:
    char *_data;
    size_t _len;

    // ...

public:
    // move construct
    MyString(MyString &&str) noexcept : _data(str._data), _len(str._len)
    { }

    // move assignment
    MyString &operator=(MyString &&str) noexcept
    {
        // ...
        return *this;
    }
};
  • 注: growable conatiner (会发生 memory reallocation) 只有两种:vector 和 deque

vector:

deque:

override 关键字

在成员函数声明或定义中,override确保该函数为虚函数并覆盖某个基类中的虚函数。若此非真则程序生成编译错误。

struct Base
{
    virtual void vfunc(float)
    { }
};

struct Derived1 : public Base
{
    // 当打算覆盖基类函数时意外地创建了一个新的虚函数。这是一个普遍的问题,尤其是当用户去修改基类时
    virtual void vfunc(int)  // error: ‘virtual void Derived::vfunc(int)’ marked ‘override’, but does not override
    { } 
};

struct Derived : public Base
{
    // 覆盖意味着编译器将检查基类,以查看是否存在具有此确切签名的虚函数。 如果没有,编译器将指示错误。
    virtual void vfunc(int) override  // error: ‘virtual void Derived::vfunc(int)’ marked ‘override’, but does not override
    { }  

    virtual void vfunc(float) override  // OK
    { }
};

final 关键字

当在虚函数声明或定义中使用时,final确保函数为虚并指定其不可被派生类覆盖。否则生成编译时错误。

struct Base final
{ };

struct Derived : Base  // error: cannot derive from ‘final’ base ‘Base’ in derived type ‘Derived’
{ };

当在类定义中使用时,final指定此类不可在另一类的定义中的基类说明符列表中出现(换言之,不能派生于它)。否则生成编译时错误。

struct Base
{
    virtual void f() final;  // error: virtual function ‘virtual void Derived::f()’void f();
};

struct Derived : public Base
{
    void f();  // error: virtual function ‘virtual void Derived::f()’ void f();

};

decltype 关键字

declval converts and type to a
reference type, making it possible to use member functions in
decltype expression without the need to go through constructors.

(declval将类型转换为引用类型,这样就可以在decltype表达式中使用成员函数,而不需要通过构造函数。)

decltype allows the compiler to infer the
return type of a function base on an arbitrary expression and makes
perfect forwarding more generic. In past version, for two arbitrary types T1 and T2, there was no way to deduce
the type of an expression that used these two types. The decltype feature allows you to state, for example, an expression that has template argument, such as sum<T1, T2>() has the type T1+T2

(decltype允许编译器根据任意表达式推断函数的返回类型,并使更完美的转发更为通用。 在过去的版本中,对于两个任意类型T1和T2,无法推断使用这两种类型的表达式的类型。 decltype功能允许您声明例如具有模板参数的表达式,例如sum <T1,T2>()的类型为T1 + T2)

By using the new decltype keyword, you can
let the compiler find out the type of an expression. This is the realization of the often realization typeof feature. However, the existing typeof implementations were incomsistent and incomplete, so C++11 introduced a new keyword. For example:

(通过使用新的decltype关键字,你可以让编译器找出表达式的类型。 这是通常实现的typeof功能的实现。 但是,现有的typeof实现不兼容且不完整,因此C ++ 11引入了新的关键字。 例如:)

注: typeof 仅为 GNU C编译器的实现,并非C++的标准规格。

map<string, flot> coll;
decltype(coll)::val_type ele;

// C++11 之前
map<string, float>::value_type elem;

decltype 的主要应用

defines a type equivalent to the type of an expression

(定义与表达式类型等效的类型)

By using the new decltype keyword, you can let the compier find out the type of an expression. This is the realization of the often requested typeof feature.

(通过使用新的decltype关键字,可以让编译器找出表达式的类型。 这是经常需要的typeof功能的实现)

One application of decltype is [(1) to declare return types]. Another is use it [(2) in metaprogramming] or to [(3) pass the type of a lamdba].

(decltype的一种应用是声明返回类型。 另一个是在元编程中使用它或传递lamdba的类型。)

应用一:声明返回类型

Something,
the return type of a function depends on
an expression processed with the arguments. However, something like

(在某种程度上,函数的返回类型取决于使用参数处理的表达式。 但是,类似)

template <typename T1, typename T2>
decltype(x+y) add(T1 x, T2 x);  // 编译错误!!

was not possible before C++11,
because the return expression uses objects not introduced in scope yet. But with C++11, you can alterbativaly declare the return type of a function behind the parameter list:

(在C ++ 11之前是不可能的,因为返回表达式使用的对象尚未在作用域中引入。 但是使用C ++ 11时,您可以在参数列表后面随意声明一个函数的返回类型:)

template <typename T1, typename T2>
auto add(T1 x, T2 y) ->decltype(x + y)
  • 在标准库中的使用

应用二:模板元编程

// 传入的 obj 对象应该是容器
template <typename T>
void test_decltype(T obj)
{
    // 1. 
    map<string, float>::value_type elem1;

    map<string, float> coll;
    decltype(coll)::value_type elem2;

    // 2.
   typedef typename decltype(obj)::iterator iType1;
   // ==>
   typedef typename T::itorator iType2;

   // 3.
   decltype(obj) anotherObj(obj);
}

应用三: 传递 lambda 类型

auto cmp = [] (const Person &p1, const Person &p2)
                        {
                            return p1.lastname() < p2.lastname()
                               || (p1.lastname() == p2.lastname() 
                                  && p1.firstname() < p2.lastname());

                        };

std::set<Person, decltype(cmp)> coll(cmp);

面对 lambda ,我们手上往往只有 object,没有 type,要获得 type 就得借助于 decltype。

Lambdas

Part1

C++ introduced lambdas, allowing the
definition of inline functionality, which can
be used as a parameter or a local object. lambdas change the way the C++ standard library is used.

(C ++引入了lambda,允许定义内联功能,这些功能可用作参数或本地对象。 lambdas更改了C ++标准库的使用方式。)

A lambda is
a definition of functionality that can be
defined inside statements and expressions. Thus, you can use a lambda
as an inline function. The minimal lambda function has no parameters and simple does someting.

(Lambda是功能的定义,可以在内部语句和表达式中定义。因此,你可以将lambda用作内联函数。 最小lambda函数没有参数,并且简单地完成一些操作。)

[] { 
    std::cout << "hello lambda" << srd::endl;
};

可以直接调用它:

[] { 
    std::cout << "hello lambda" << srd::endl;
}(); // printf "hello lambda"

或将其传递给对象以被调用:

auto L = [] {
    std::cout << "hello lambda" << srd::endl;
};

L(); // printf "hello lambda" 

Part2

[capture list](params list) mutableopt throwSpecopt ->retTypeopt {function body}

  • capture list:捕获外部变量列表
  • params list:形参列表
  • mutable指示符:用来说明是否可以修改捕获的变量
  • exception:异常说明
  • return type:返回类型
  • function body:函数体

capture list

capture to access
nonstatic outside objects inside the lambda. Static objects such as std::cout can be used.

(捕获以访问lambda内部的非静态外部对象。 可以使用诸如std :: cout之类的静态对象。)

you can specify a capture to access
data of outer scope that is not passed as an argument.

(您可以指定捕获以访问未作为参数传递的外部范围数据。)

[ ] 不捕获任何外部变量
[变量名, …] 默认以值得形式捕获指定的多个外部变量(用逗号分隔),如果引用捕获,需要显示声明(使用&说明符)
[this] 以值的形式捕获this指针
[=] 以值的形式捕获所有外部变量
[&] 以引用形式捕获所有外部变量
[=, &x] 变量x以引用形式捕获,其余变量以传值形式捕获
[&, x] 变量x以值的形式捕获,其余变量以引用形式捕获

params list

All of them are option, but if one of the occurs, the parentheses for the parameters are mandatory.

(它们都是可选的,但是如果其中一种发生,则参数的括号是强制性的。)

mutable

objects are passed by value, but inside the function object defined by the lambda, you have write access to the passed value.

(对象按值传递,但是在lambda定义的函数对象内部,你可以对传递的值进行写访问。)

throwSpec

提供异常说明

retType

without any specific definition of the return type, it is deduced from the return value.

(在没有对返回类型进行任何特定定义的情况下,将从返回值中推导出来。)

Part3

The type of a lambda is an anonymous function object (or functor)

(匿名函数对象)

// objects are passed by value, but inside the function object defined by the lambda, 
// you have write access to the passed value.
void func1()
{
    int id = 0;
    auto f = [id] () mutable {
        cout << id << " ";
        id++;
    };

    id = 42;

    f();
    f();
    f();

    cout << id << endl;  // print : 0 1 2 42: 
};
void func2()
{
    int id = 0;
    auto f = [&id] () {
        cout << "id: " << id << endl;
        id++;
    };

    id = 42;

    f();
    f();
    f();

    cout << id << endl;  // print : 42 43 44 45
}
void func3()
{
    int id = 0;
    auto f = [id] () {
        cout << "id: " << id << endl;
        id++;  // error: increment of read-only variable ‘id’
    };

    id = 42;

    f();
    f();
    f();

    cout << id << endl;
};
void func4()
{
    int id = 0;
    auto f = [id]   () mutable {
        cout << "id: " << id << endl;
        id++;

        static int x = 5;  // 可以声明变量
        int y = 6;         // 可以声明返回值
        return id;
    };
    
    f();
}

Part4

当我们编写一个 lambda后, 编译器将该表达时翻译成一个未命名的未命名对象。再 lambda 表达时产生的类中含有一个重载的函数调用运算符。默认情况下 lambda 不能改变它捕获的变量。因此在默认情况下,由 lambda 产生的类当中的函数调用运算符是一个 const 成员函数。如果 lambda 被声明为 mutable, 则调用运算符就不是 const 的了。

int tobefound = 5;
auto lambda1 = [tobefound] (int val) {return val == tobefound;};

class UnNameLocalFunction
{
    int locaVal;
public:   
    UnNameLocalFunction(int var) : locaVal(var) 
    { }

    bool operator () (int val) const
    {
        return val = locaVal;
    }
};

UnNameLocalFunction lambda2(tobefound);

bool b1 = lambda1(5);
bool b2 = lambda2(5);

Part5

The type of a lambda is an anonymous function object(or functor) that is unique for each lambda expression. Thus, to declare object of that type, you need templates or auto. If you need the type, you can use decltype(), which is, for example, required to pass a lambda as hash function or ordering or sorting critrtion to associative or unordered containers.

(Lambda的类型是一个匿名函数对象,对于每个Lambda表达式都是唯一的。 因此,要声明该类型的对象,您需要templates或auto。 如果需要该类型,则可以使用decltype(), 例如,需要将lambda作为散列函数传递,或者将排序或排序规则传递给关联的或无序的容器;)

auto cmp = [] (const Person &p1, const Person &p2)
                        {
                            return p1.lastname() < p2.lastname()
                               || (p1.lastname() == p2.lastname() 
                                  && p1.firstname() < p2.lastname());

                        };

std::set<Person, decltype(cmp)> coll(cmp);

set 实现:

template <class Key,
          class Compare = less<Key>,
          class Alloc = alloc>
class set
{
public:
    // typedes:
    ...
    typedef Compare key_compare;
    typedef Compare value_compare;

private:
    typedef rb_tree<key_type, value_type,
                    identity<value_type>,
                    key_compare, Alloc> rep_type;

    rep_type t; // red-black tree representing set

public:
    ...
    set() : t(Compare()) {}
    explicit set(const Compare &comp) : t(comp) {}
};

Because you need the type of the lambda for the declaration of the set. decltype must be used, which yields the type of a lambda object, such as cmp. Note that you alse have to pass the lambda object to the constructor of coll; otherwise, coll would call the default constructor for the sorting criterion passed, and
by rule lambdas have no default constructor and no assignment operator. So, for a ssorting criterion , a class defining the function object might still be more intuitive.

(因为你需要lambda的类型来声明set,decltype必须被使用,它产生lambda对象的类型,例如cmp。注意,你还必须将lambda对象传递给coll的构造函数; 否则,coll将为传递的排序条件调用默认构造函数,并且lambdas规则没有默认构造函数和赋值运算符。 因此,对于排序准则,定义函数对象的类可能仍然更加直观。)

lambda 表达式产生的类不包含默认构造函数、赋值运算符及默认析构函数。

Part6

Function objects are a very powerful way to `customize the behavior of Standard Template Library(STL) algorithms, and can encapsulate both code and data` (unlike plain functions). But function objects are inconvenient to define because of the need to write entire classes. Moreover, they are not defined in the place in your source code where you're trying to use them, and the non-locality makes them more difficult to use. Libraries have attempted to mitigate some of the problems of verbosity and non-locality, but don't offer much help because the syntax becomes complicated and the  compiler error are not very friendly. Using function objects from libraries is also less efficient since the function objects defined as data members are `not in-line`.

(函数对象是一种非常强大的方法,可自定义标准模板库(STL)算法的行为,并且可以封装代码和数据(与普通函数不同)。 但是由于需要编写整个类,因此函数对象的定义不方便。 而且,它们没有在您试图使用它们的源代码中定义,并且非本地性使它们更难使用。 库已经尝试减轻一些冗长和非局部性的问题,但是由于语法变得复杂并且编译器错误不是很友好,因此没有提供太多帮助。 使用库中的函数对象的效率也较低,因为定义为数据成员的函数对象不是内联的。)

Lambda expressions address these problems. The following code snippet shows a lambda expression used in a program to remove integers between variables x and y from a vector integers.

(Lambda表达式解决了这些问题。 以下代码段显示了一个lambda表达式,该表达式在程序中用于从向量整数中删除变量x和y之间的整数。)

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void func1()
{
    vector<int> vi {5, 28, 50, 83, 70, 590, 245, 59, 24};
    int x = 30;
    int y = 100;

    vi.erase(remove_if(vi.begin(),
                        vi.end(),
                        [x, y](int n) {return x<n && n<y;}
                        ),
              vi.end()
             );

    for (auto i : vi)
        cout << i << ' ';
    cout << endl;
}

void func2()
{
    class LambdaFunctor
    {
    public:
        LambdaFunctor(int a, int b) : m_a(a), m_b(b)
        { }

        bool operator() (int n) const
        {
            return m_a < n && n < m_b;
        }

    private:
        int m_a;
        int m_b;
    };

    vector<int> vi {5, 28, 50, 83, 70, 590, 245, 59, 24};
    int x = 30;
    int y = 100;

    vi.erase(remove_if(vi.begin(),
                        vi.end(),
                        LambdaFunctor(x, y)),
              vi.end()
             );

    for (auto i : vi)
        cout << i << ' ';
    cout << endl;
}

int main()
{
    func1();

    func2();

    return 0;
}

输出:

5 28 590 245 24
5 28 590 245 24

在标准库中的使用

作者:TianSong