跳转至

C++关键字

delctype

  • 功能:通过表达式或对象来识别类型
  • 示例
struct A { double x; };
const A* a;

decltype(a->x) y;       // y 的类型是 double(其声明类型)
decltype((a->x)) z = y; // z 的类型是 const double&(左值表达式)

template<typename T, typename U>
auto add(T t, U u) -> decltype(t + u) // 返回类型依赖于模板形参
{                                     // C++14 开始可以推导返回类型
    return t+u;
}

constexpr

  • 功能:在编译期计算函数或表达式的值
  • 使用场景:如果能在编译期就能确定的值,可以使用constepr。
  • 条件
  • 其表达式或函数的参数必须是字面量值,比如数值1,2,3,或字符串“1234”
  • 表达式或函数参数也是constexpr表达式。
  • 示例
#include <iostream>
#include <stdexcept>

// C++11 constexpr 函数使用递归而非迭代
// (C++14 constexpr 函数可使用局部变量和循环)
constexpr int factorial(int n)
{
    return n <= 1? 1 : (n * factorial(n - 1));
}

// 字面类
class conststr {
    const char* p;
    std::size_t sz;
public:
    template<std::size_t N>
    constexpr conststr(const char(&a)[N]): p(a), sz(N - 1) {}

    // constexpr 函数通过抛异常来提示错误
    // C++11 中,它们必须用条件运算符 ?: 这么做
    constexpr char operator[](std::size_t n) const
    {
        return n < sz ? p[n] : throw std::out_of_range("");
    }
    constexpr std::size_t size() const { return sz; }
};

// C++11 constexpr 函数必须把一切放在单条 return 语句中
// (C++14 无此要求)
constexpr std::size_t countlower(conststr s, std::size_t n = 0,
                                             std::size_t c = 0)
{
    return n == s.size() ? c :
           'a' <= s[n] && s[n] <= 'z' ? countlower(s, n + 1, c + 1) :
                                        countlower(s, n + 1, c);
}

// 输出要求编译时常量的函数,用于测试
template<int n>
struct constN
{
    constN() { std::cout << n << '\n'; }
};

int main()
{
    std::cout << "4! = " ;
    constN<factorial(4)> out1; // 在编译时计算

    volatile int k = 8; // 不允许使用 volatile 者优化
    std::cout << k << "! = " << factorial(k) << '\n'; // 运行时计算

    std::cout << "the number of lowercase letters in \"Hello, world!\" is ";
    constN<countlower("Hello, world!")> out2; // 隐式转换到 conststr
}

mutable

  • 功能:使定义为const的对象内的成员能被修改
  • 使用场景:如果A中有一个成员m被声明为mutable,当const A a;时,使a.m=value能执行
  • 示例
int main()
{
    int n1 = 0;           // 非 const 对象
    const int n2 = 0;     // const 对象
    int const n3 = 0;     // const 对象(同 n2)
    volatile int n4 = 0;  // volatile 对象
    const struct
    {
        int n1;
        mutable int n2;
    } x = {0, 0};      // 带 mutable 成员的 const 对象

    n1 = 1; // ok,可修改对象
//  n2 = 2; // 错误:不可修改对象
    n4 = 3; // ok,当做副效应
//  x.n1 = 4; // 错误:const 对象的成员为 const
    x.n2 = 4; // ok,const 对象的 mutable 成员不是 const

    const int& r1 = n1; // 绑定到非 const 对象的 const 引用
//  r1 = 2; // 错误:试图通过到 const 的引用修改
    const_cast<int&>(r1) = 2; // ok,修改非 const 对象 n1

    const int& r2 = n2; // 绑定到 const 对象的 const 引用
//  r2 = 2; // 错误:试图通过到 const 的引用修改
//  const_cast<int&>(r2) = 2; // 未定义行为:试图修改 const 对象 n2
}

volatile

  • 功能:禁止编译器的优化
  • 使用场景:
  • static volatile 对象模仿映射于内存的 I/O 端口,而 static const volatile 对象模仿映射于内存的输入端口
  • sig_atomic_t 类型的 static volatile 对象用于与 signal 处理交流
  • 含有对 setjmp 宏调用的函数中的局部 volatile 变量,是在 longjmp 返回后仅有保证恢复其值的局部变量
  • volatile 变量可用于禁用某些优化形式,例如禁用死存储消除,或为微基准禁用常量折叠
  • 示例
#include <stdio.h>
#include <time.h>

int main(void)
{
    clock_t t = clock();
    double d = 0.0;
    for (int n = 0; n < 10000; ++n)
        for (int m = 0; m < 10000; ++m)
            d += d * n * m; // 读写非 volatile 对象
    printf("Modified a non-volatile variable 100m times. "
           "Time used: %.2f seconds\n",
           (double)(clock() - t)/CLOCKS_PER_SEC);

    t = clock();
    volatile double vd = 0.0;
    for (int n = 0; n < 10000; ++n)
        for (int m = 0; m < 10000; ++m) {
            double prod = vd * n * m; // 读 volatile 对象
            vd += prod; // 读写 volatile 对象
        } 
    printf("Modified a volatile variable 100m times. "
           "Time used: %.2f seconds\n",
           (double)(clock() - t)/CLOCKS_PER_SEC);
}
//可能的输出
//Modified a non-volatile variable 100m times. Time used: 0.00 seconds
//Modified a volatile variable 100m times. Time used: 0.79 seconds

const volatile

  • 功能:禁止由代码中改变值(允许由外部条件,如硬件改变),同时声明volatile禁止编译器的优化(即每一次读取数据都要原内存地址去读)
  • 使用场景:不允许由程序员通过代码去改变值,但可能由硬件等外部改变的值,因此又不能让编译器去优化,否则会导致读出的值是未更新的原始值。

noexcept

  • 功能:
  • 作为运算符noexcept( 表达式 ):运算符进行编译时检查,若表达式声明为不抛出任何异常则返回 true
  • 作为说明符:指定函数是否抛出异常
  • 示例
#include <iostream>
#include <utility>
#include <vector>

void may_throw();
void no_throw() noexcept;//说明符
auto lmay_throw = []{};
auto lno_throw = []() noexcept {};
class T{
public:
  ~T(){} // 析构函数妨碍了移动构造函数
         // 复制构造函数为 noexcept
};
class U{
public:
  ~U(){} // 析构函数妨碍了移动构造函数
         // 复制构造函数为 noexcept(false)
  std::vector<int> v;
};
class V{
public:
  std::vector<int> v;
};

int main()
{
 T t;
 U u;
 V v;
 //以下noexcept作为运算符
 std::cout << std::boolalpha
           << "Is may_throw() noexcept? " << noexcept(may_throw()) << '\n'
           << "Is no_throw() noexcept? " << noexcept(no_throw()) << '\n'
           << "Is lmay_throw() noexcept? " << noexcept(lmay_throw()) << '\n'
           << "Is lno_throw() noexcept? " << noexcept(lno_throw()) << '\n'
           << "Is ~T() noexcept? " << noexcept(std::declval<T>().~T()) << '\n'
           // 注:以下各项测试也要求 ~T() 为 noexcept
           // 因为 noexccept 中的表达式构造并销毁了临时量
           << "Is T(rvalue T) noexcept? " << noexcept(T(std::declval<T>())) << '\n'
           << "Is T(lvalue T) noexcept? " << noexcept(T(t)) << '\n'
           << "Is U(rvalue U) noexcept? " << noexcept(U(std::declval<U>())) << '\n'
           << "Is U(lvalue U) noexcept? " << noexcept(U(u)) << '\n'  
           << "Is V(rvalue V) noexcept? " << noexcept(V(std::declval<V>())) << '\n'
           << "Is V(lvalue V) noexcept? " << noexcept(V(v)) << '\n';  
}

operator

  • 功能
  • 重载运算符
  • 用户定义转换函数
  • 使用场景
  • 当需要对类中某一运算符特殊处理时
  • 当需要对类进行类型转换时,比如,类型A转类型B
  • 注意
  • 当重载运算符时,返回值类型在关键字operator之前;当进行类型转换时,返回类型在operator关键字之后。
  • 示例
class A 
{
public:
    A() :m_a(0) {}
    A(const int& a) { m_a = a; }
    //用户定义转换函数 int转换函数
    operator int () {
        return this->m_a;
    }
    //or
    //operator int& () {
    //    return this->m_a;
    //}
    operator int*() {
        return &this->m_a;
    }

    //重载 取地址符&
    A* operator &() {
        return this;
    }
private:
    int m_a;
};
int main()
{
    A a(2);
    //int转换函数
    int i = (int)(a);
    i = static_cast<int>(a);
    //int*转换函数
    int* pi = (int*)(a);
    //使用重载运算符&
    A* pa = &a;
}