C++ literal_C++字面量常量语法详解

字面量类型由后缀和上下文共同决定:整数如123默认为int(若可容纳),123ULL强制为unsigned long long;浮点如1.0为double、1.0f为float;字符串字面量类型是const char[N],非std::string;UDL后缀须以下划线开头且函数需constexpr;nullptr类型为std::nullptr_t。

字面量的类型由后缀和上下文共同决定

不加修饰的数字字面量,比如 1233.14,类型不是“默认 int”或“默认 double”——C++ 标准只规定了最小保证范围,具体类型取决于字面量形式和编译器实现。例如:123int(如果 int 能容纳),但 1234567890123 在 32 位系统上就可能是 long long123U 明确是 unsigned int,而 123ULL 强制为 unsigned long long

浮点字面量同理:1.0double1.0ffloat1.0Llong double。漏写 f 可能在 float 比较中引发隐式转换和精度丢失,比如 float x = 0.1; 实际存储的是近似值,而 0.1f == 0.1false(后者是 double)。

字符串字面量的类型是 const char[N],不是 std::string

"hello" 得到的是一个以 \0 结尾的字符数组,类型为 const char[6](含终止符)。它不能直接赋给 std::string 变量而不触发构造(虽然语法允许,那是调用了隐式构造函数),更不能赋给 char*(C++11 起禁止非 const 转换)。

  • const char* p = "hello"; ✅ 合法
  • char* p = "hello"; ❌ 编译错误(弃用的转换)
  • std::string s = "hello"; ✅ 调用 std::string(const char*) 构造
  • auto s = "hello";s 类型是 const char[6],不是 std::string

多行字符串必须用反斜杠续行或用原始字符串字面量:R"(line1\nline2)",否则 "line1\nline2" 中的换行会破坏语法。

用户定义字面量(UDL)要小心返回类型和求值时机

定义像 123_km 这样的字面量,本质是调用一个带特定后缀名的 constexpr 函数。它必须满足:参数类型只能是 unsigned long longlong doubleconst char*charwchar_t 等有限几种,且函数必须是 constexpr 才能用于常量表达式。

constexpr long long operator"" _km(unsigned long long val) {
    return static_cast(val * 1000);
}
// 使用
constexpr auto d = 5_km; // ✅ 编译期计算
int x = 5_km;            // ✅ 运行时也行,但失去 constexpr 优势

常见陷阱:

  • 后缀名必须以 underscore 开头(如 _km),否则编译失败
  • 返回类型若不是字面量类型(如含非静态成员的类),就不能用于 constexpr 上下文
  • 字符串 UDL(operator"" _s(const char*, size_t))参数是裸指针 + 长度,不带终止符,别直接当 C 字符串用

布尔和空指针字面量没有歧义,但要注意模板推导

truefalse 类型固定为 bool,不会退化成整数;nullptr 类型是 std::nullptr_t,专用于指针比较,比 NULL(常定义为 0((void*)0))更安全。

但在模板函数中,nullptr 的类型推导可能出人意料:

template
void f(T) { }

f(nullptr); // T 推导为 std::nullptr_t,不是 void* f(0); // T 推导为 int

所以重载函数里区分指针和整数时,void f(std::nullptr_t)void f(void*) 更精准;而用 auto 推导时,auto p = nullptr; 得到的是 std::nullptr_t,不是指针类型,不能解引用。

字面量看着简单,但类型细节一旦错位,轻则隐式转换带来性能损耗,重则模板匹配失败或 constexpr 场景编译不过。最易被忽略的是字符串字面量的数组类型本质,以及 UDL 后缀命名规则和 constexpr 约束。