48、C++11新特性(nullptr与constexpr):空指针常量、编译期常量

说实话,C++11之前,处理空指针和常量表达式这两件事,我一直觉得有点「别扭」。

先说说空指针。以前我们用 NULL 或者直接写 0。但 NULL 在 C++ 里其实是个宏,通常被定义为 0 或者 (void*)0。这就带来了一个很尴尬的问题——函数重载的时候,编译器会搞混。

核心要点:nullptr 解决了空指针的类型安全问题,constexpr 让编译期计算变得优雅可控。

nullptr:终于有了真正的空指针类型

我记得刚入行那会儿,写过这样的代码:

void foo(int) {
    cout << "调用了 int 版本" << endl;
}

void foo(char*) {
    cout << "调用了指针版本" << endl;
}

int main() {
    foo(NULL);   // 猜猜调用了哪个?
    foo(nullptr); // 这个呢?
}

猜到了吗?foo(NULL) 实际上调用了 foo(int)。因为 NULL 就是 0,编译器优先匹配了整型版本。这在我早期的一个项目里引发过 bug——我明明想传空指针,结果走进了整型分支。

nullptr 的出现就是为了终结这种混乱。它的类型是 std::nullptr_t,可以隐式转换为任何指针类型,但绝不会被当作整型。

我个人习惯,从 C++11 开始,所有空指针一律用 nullptr,不再碰 NULL。你想想看,代码里写 nullptr,读起来也更直观——「哦,这是个空指针」。而 NULL 总让人多犹豫半秒。

小技巧:如果你在模板代码里需要判断一个指针是否为空,用 nullptr 配合 if constexpr(C++17)效果更佳。

constexpr:把计算搬到编译期

再说 constexpr。这个关键字我一开始觉得有点多余——不是有 const 吗?

后来我明白了。const 只是承诺「这个值不会变」,但它的值可能是在运行时才确定的。而 constexpr 要求「这个值必须在编译期就能算出来」。

举个例子:

constexpr int factorial(int n) {
    return n <= 1 ? 1 : n * factorial(n - 1);
}

int main() {
    constexpr int val = factorial(5); // 编译期就算好了
    int arr[val]; // 可以用作数组大小
}

这段代码里,factorial(5) 在编译期就计算出了 120。val 是编译期常量,所以可以用来定义数组大小。这在 C++98 里是做不到的——那时候数组大小必须是字面量。

我在项目中遇到过一种场景:需要预计算一组查表数据,比如正弦值。用 constexpr 函数,可以在编译期生成整个数组,运行时直接查表,性能提升很明显。

注意:constexpr 函数在 C++11 中限制较多——函数体只能有一条 return 语句。C++14 放宽了这个限制,可以包含循环和局部变量。C++17 和 C++20 又进一步扩展了能力。

constexpr 与 nullptr 的配合

这两个特性看似独立,其实在泛型编程和模板元编程里经常一起出现。比如:

template <typename T>
constexpr T* get_null() {
    return nullptr;
}

int main() {
    int* p = get_null<int>();
    double* q = get_null<double>();
}

这里 nullptr 提供了类型安全的空指针,constexpr 保证了函数在编译期求值。两者结合,写出来的代码既安全又高效。

避坑指南

我曾经踩过一个坑:在 constexpr 函数里调用了非 constexpr 函数。编译器直接报错,说「这个函数不能用于常量表达式」。嗯,当时我一脸懵。

后来养成了习惯:写 constexpr 函数时,确保它依赖的所有函数也都是 constexpr 的。否则,这个函数在运行时可以调用,但在编译期求值时就会失败。

另外,nullptr 虽然好,但别滥用。比如:

int* p = nullptr;
delete p; // 删除空指针是安全的,但没必要

删除空指针是合法的,但如果你每次都要检查指针是否为空,那说明设计上可能有问题。我个人建议:用智能指针代替裸指针,让生命周期管理更清晰。

知识体系图

C++11 新特性 nullptr 类型安全 重载决议正确 隐式转换 constexpr 编译期计算 数组大小 模板元编程 共同目标:更安全、更高效的代码

总结一下

nullptrconstexpr 是 C++11 里两个看似简单、实则影响深远的特性。一个解决了空指针的类型安全问题,一个让编译期计算变得可控且可读。

我个人觉得,这两个特性是 C++ 从「带类的 C」走向「现代 C++」的重要标志。如果你还在用 NULLconst 硬扛,不妨试试 nullptrconstexpr——写起来更舒服,跑起来更放心。

一句话记住:空指针用 nullptr,编译期常量用 constexpr。别问为什么,问就是 C++11 的良心。


公众号:蓝海资料掘金营,微信deep3321