C++11 带来的新特性 (3)—— 关键字noexcept

1 关键字noexcept

从C++11开始,咱们能看到不少代码当中都有关键字noexcept。好比下面就是std::initializer_list 的默认构造函数,其中使用了noexcept。 函数

constexpr initializer_list() noexcept
      : _M_array(0), _M_len(0) { }

该关键字告诉编译器,函数中不会发生异常,这有利于编译器对程序作更多的优化。
若是在运行时,noexecpt函数向外抛出了异常(若是函数内部捕捉了异常并完成处理,这种状况不算抛出异常),程序会直接终止,调用std::terminate()函数,该函数内部会调用std::abort()终止程序。优化

2 C++的异常处理

C++中的异常处理是在运行时而不是编译时检测的。为了实现运行时检测,编译器建立额外的代码,然而这会妨碍程序优化。
在实践中,通常两种异常抛出方式是经常使用的:this

  • 一个操做或者函数可能会抛出一个异常;
  • 一个操做或者函数不可能抛出任何异常。

后面这一种方式中在以往的C++版本中经常使用throw()表示,在C++ 11中已经被noexcept代替。code

void swap(Type& x, Type& y) throw()   //C++11以前
    {
        x.swap(y);
    }
    void swap(Type& x, Type& y) noexcept  //C++11
    {
        x.swap(y);
    }

3 有条件的noexcecpt

在第2节中单独使用noexcept,表示其所限定的swap函数绝对不发生异常。然而,使用方式能够更加灵活,代表在必定条件下不发生异常。ci

void swap(Type& x, Type& y) noexcept(noexcept(x.swap(y)))    //C++11
    {
        x.swap(y);
    }

它表示,若是操做x.swap(y)不发生异常,那么函数swap(Type& x, Type& y)必定不发生异常。编译器

一个更好的示例是std::pair中的移动分配函数(move assignment),它代表,若是类型T1和T2的移动分配(move assign)过程当中不发生异常,那么该移动构造函数就不会发生异常。it

pair& operator=(pair&& __p)
    noexcept(__and_<is_nothrow_move_assignable<_T1>,
                    is_nothrow_move_assignable<_T2>>::value)
    {
        first = std::forward<first_type>(__p.first);
        second = std::forward<second_type>(__p.second);
        return *this;
    }

4 何时该使用noexcept?

使用noexcept代表函数或操做不会发生异常,会给编译器更大的优化空间。然而,并非加上noexcept就能提升效率,步子迈大了也容易扯着蛋。
如下情形鼓励使用noexcept:io

  • 移动构造函数(move constructor)
  • 移动分配函数(move assignment)
  • 析构函数(destructor)。这里提一句,在新版本的编译器中,析构函数是默认加上关键字noexcept的。下面代码能够检测编译器是否给析构函数加上关键字noexcept。
struct X
    {
        ~X() { };
    };
    
    int main()
    {
        X x;
    
        // This will not fire even in GCC 4.7.2 if the destructor is
        // explicitly marked as noexcept(true)
        static_assert(noexcept(x.~X()), "Ouch!");
    }
  • 叶子函数(Leaf Function)。叶子函数是指在函数内部不分配栈空间,也不调用其它函数,也不存储非易失性寄存器,也不处理异常。

最后强调一句,在不是以上状况或者没把握的状况下,不要轻易使用noexception。编译

相关文章
相关标签/搜索