[c++11]常量

参考:

Numeric, Boolean and Pointer Literals (C++)

String and character literals (C++)

User-Defined Literals (C++)

常量(literal)指的是直接表示值的程序元素,常用于初始化命名变量和将参数传递给函数。按类型可分为以下几种:

  1. 整数常量(integer literal
  2. 浮点数常量(floating-point literal
  3. 布尔常量(boolean literal
  4. 指针常量(pointer literal
  5. 字符常量(character literal
  6. 字符串常量(string literal
  7. 自定义常量(user-defined literal

如果要指定特定的常量类型,可以为其添加前缀(prefix)和后缀(suffix)。比如16进制值0x35,长整型数123L

整数常量

  • 整型常量以数字开头,没有小数或指数部分
  • 可以指定十进制(Decimal)、八进制(Octal)或十六进制(Hexadecimal)形式的整型常量
  • 可以指定有符号(unsigned)或无符号(unsigned)类型以及长(long)或短(short)类型

默认整数常量的类型是int32位),如果常量超出32位,则给定类型为long long64位)

八/十/十六进制

八进制整数常量以0开头,十六进制整数常量以0x开头,十进制整数常量没有前缀

    int a = 32;
    int b = 040;
    int c = 0x20;

    // 以十进制输出
    cout << a << endl;
    cout << b << endl;
    cout << c << endl;

    // 以各自进制输出
    cout << std::dec << a << endl;
    cout << std::oct << b << endl;
    cout << std::hex << c << endl;

整数常量类型

  • 指定无符号整数类型,加上前缀uU
  • 指定长整数类型,加上后缀lL。比如指定64位整数类型,加上llLL后缀

数字分隔符

为便于阅读,可以使用单引号字符(single-quote)为较大的数字分隔位置。分隔符对编译没有影响

    int i = 123'456'789;
    long j = 0x123'456'789;

    cout << i << endl;
    cout << std::hex << j << endl;

浮点数常量

浮点数常量必须包含小数部分,必须包含小数点(.)并且可以包含指数

  • 使用尾数指定常量的小数部分
  • 使用指数(exponent)指定常量的量级(magnitude):将数字大小指定为10的幂

指数

使用eE指定,后跟可选符号(+-)以及一串数字。如果指数存在可以不包含小数点

    // a = 1220
    double a = 1.22e3;
    cout << a << endl;

    // b = 0.00122
    double b = 1.22e-3;
    cout << b << endl;

浮点数常量类型

默认浮点数常量为double类型(64位),可以使用后缀名f或者l(也可以使用大写形式)分别指定为float32位)或long double64位)

虽然long doubledouble具有相同的表示形式,但它们不是相同的类型

float a = 1.22e3f;
long double b = 1.22e-3l;

布尔常量

布尔常量是truefalse

    bool a = true;
    bool b = false;

    // 整数 1
    cout << a << endl;
    // 整数 0
    cout << b << endl;

指针常量

c++11开始新增了一个指针常量 - nullptr

字符常量

字符常量由常量字符组成,它由单引号包围的字符表示。有五种类型的字符常量:

  1. 普通的char类型的字符常量,比如'a'
  2. utf-8格式的char类型字符常量,比如u8'a'c++20开始)
  3. wchar_t类型的宽字符常量,比如L'a'
  4. utf-16格式的char16_t类型字符常量,比如u'a'
  5. utf-32格式的char32_t类型字符常量,比如U'a'

用于字符常量的字符可以是任何字符,除了保留字符反斜杠/、单引号'或换行符\n,不过可以使用转义序列指定保留字符;也可以使用通用字符名指定字符,只要类型足够大以容纳字符

转义序列

常用的转义序列包括

  • 换行(newline)- \n
  • 反斜杠(backslash)- \\
  • 水平制表符(horizontal tab)- \t
  • 垂直制表符(vertical tab)- \v
  • 单引号(single quote)- \'
  • 双引号(double quote)- \"
  • 空字符(the null character)- \0
  • 空格(backspace)- \b
  • 问号(question mark)- ?\?
    char newline = '\n';
    char tab = '\t';
    char backspace = '\b';
    char backslash = '\\';
    char nullChar = '\0';

    cout << "Newline character: " << newline << "ending" << endl; // Newline character:
    //  ending
    cout << "Tab character: " << tab << "ending" << endl; // Tab character : ending
    cout << "Backspace character: " << backspace << "ending" << endl; // Backspace character : ending
    cout << "Backslash character: " << backslash << "ending" << endl; // Backslash character : \ending
    cout << "Null character: " << (void *) nullChar << "ending" << endl; //Null character:  ending

字符串常量

字符串常量表示以空字符结尾的字符序列,且字符必须用双引号括起来

    // 赋值字符数组
    char a[3] = "12";
    // 赋值字符指针
    const char *b = "12";
    // 可以添加后缀s表示转换成std::string
    std::string ss = "12"s;

自定义常量

5种主要类别的常量:整数、浮点数、字符、字符串和布尔常量。从c++11开始,可以利用这些常量进行自定义,为常见习惯用法(common idioms)提供语法快捷方式并提高类型安全性

用户定义的常量没有性能优势或劣势;它们主要是为了方便或编译时的类型推导。标准库提供了自定义常量类型std::string、std::complex

用户定义的常量运算符签名

在命名空间范围内使用以下形式操作operator"",可以实现用户自定义常量:

ReturnType operator "" _a(unsigned long long int);   // Literal operator for user-defined INTEGRAL literal
ReturnType operator "" _b(long double);              // Literal operator for user-defined FLOATING literal
ReturnType operator "" _c(char);                     // Literal operator for user-defined CHARACTER literal
ReturnType operator "" _d(wchar_t);                  // Literal operator for user-defined CHARACTER literal
ReturnType operator "" _e(char16_t);                 // Literal operator for user-defined CHARACTER literal
ReturnType operator "" _f(char32_t);                 // Literal operator for user-defined CHARACTER literal
ReturnType operator "" _g(const     char*, size_t);  // Literal operator for user-defined STRING literal
ReturnType operator "" _h(const  wchar_t*, size_t);  // Literal operator for user-defined STRING literal
ReturnType operator "" _i(const char16_t*, size_t);  // Literal operator for user-defined STRING literal
ReturnType operator "" _g(const char32_t*, size_t);  // Literal operator for user-defined STRING literal
ReturnType operator "" _r(const char*);              // Raw literal operator
template<char...> ReturnType operator "" _t();       // Literal operator template
  • 运算符名称a/b/c/...是占位符
  • 下划线(the leading underscore)是必须的(只有标准库中的自定义常量可以不用下划线)
  • 返回值根据需要进行指定
  • 自定义常量也可被定义为constexpr

定义结构体Distance,自定义常量_km_m用于转换千米和米,

struct Distance {
public:
    long double get_meters() { return meters; }

    Distance operator+(Distance &other) {
        return Distance(get_meters() + other.get_meters());
    }

private:
    friend Distance operator "" _km(long double val);

    friend Distance operator "" _m(long double val);

    explicit Distance(long double val) : meters(val) {}

    // 定义私有成员,保存米数
    long double meters{0};
};

Distance operator "" _km(long double val) {
    return Distance(val * 1000);
}

Distance operator "" _m(long double val) {
    return Distance(val);
}

int main() {
    // Must have a decimal point to bind to the operator we defined!
    Distance d{402.0_m}; // construct using kilometers
    cout << "meters in d: " << d.get_meters() << endl; // 402

    Distance d2{4.02_m}; // construct using miles
    cout << "meters in d2: " << d2.get_meters() << endl;  // 4020

    Distance d3 = 4.2_km;
    Distance d4 = 36.0_m;
    // add distances constructed with different units
    Distance d5 = d3 + d4;
    cout << "d5 value = " << d5.get_meters() << endl; // 99.6

    // 不能调用私有构造函数
//    Distance d6(90.0); // error constructor not accessible

    string s;
    getline(std::cin, s);
    return 0;
}

常量使用规范

不推荐直接在表达式或语句中使用常量,这通常被称为魔法常量magic constants),更好的方式是使用具有明确含义的命名常量,这有助于上下文理解