Appearance
C++11~20 特性清单与 constexpr / default / delete / nullptr
用于「介绍一下 C++11 新特性/了解哪些 C++20」这类暖场的系统化回答框架。
一、C++11 ~ C++20 特性总账(回答时可分点挑)
C++11(最常考)
- 语言:auto/decltype 类型推导、右值引用与移动语义、lambda、range-for、
nullptr、enum class、constexpr、default/delete、可变参数模板、委托构造、explicit(转换)、override/final、static_assert、thread_local。 - 库:
std::thread、智能指针(unique/shared/weak)、std::function/std::bind、std::array/unordered_*/tuple、std::atomic、时间库 chrono、std::move/std::forward、std::begin/end、regex、random。
C++14
- 泛型 lambda(auto 参数)、函数返回类型
auto推导、make_unique、std::integer_sequence、变量模板、if constexpr(实际C++17)… 库:std::shared_timed_mutex等。
C++17
- 结构化绑定、if/switch 初始化语句、
inline变量(解决头文件 static/全局定义)、fold 表达式(可变参)、if constexpr、filesystem、类模板参数推导 (CTAD)。 - 库:
std::optional / variant / any、string_view、shared_mutex、std::byte、std::apply、并行算法。
C++20
- concepts(约束)、coroutines(协程)、ranges、
<=>三向比较、consteval/constinit、span、format、jthread、以 Module 起步。
面试回答策略:C++11 名 + 2~3 个展开讲原理(移动、智能指针、lambda)→ 足够;C++20 至少知道 concepts/coroutines/ranges 三件套各一两句。
二、const vs constexpr
| const | constexpr | |
|---|---|---|
| 求值期 | 运行期(虽然整型常量表达式可折叠) | 编译期求值,必须常量表达式才能给 |
| 能力 | 运行时 readonly | 编译期常量 + 可用于模板实参/数组大小/switch case |
| 函数 | 不能使普通函数变编译期 | constexpr 函数(C++11 简单、C++14 放宽,可普通语句)被要求时编译期求值,否则退化运行期 |
| 推断需要 | 简单 | 需自洽(变量须字面量等) |
- 优先用
constexpr表达「编译期常量」语义;const表达「运行期只读」即可。 - C++17
if constexpr编译期分支消除(模板里剪枝)。 - C++20
consteval强制编译期(不许运行时求值);constinit保证静态初始化期常量。
三、default / delete(放大的声明控制)
= default:显式显式请求编译器生成默认实现。用途:想让析构/拷贝/移动保留默认行为但代码里又要声明它们(便于集中成类、或阻止编译期隐式删除、或继承 special member),或想恢复被「自定义其它 special member」抑制的默认函数。= delete:删除某个函数(调用即编译错)。用途:禁止拷贝、禁止特定重载、禁止从某些类型隐式转换等:
cpp
class NoCopy {
public:
NoCopy() = default;
NoCopy(const NoCopy&) = delete; // 禁拷贝
NoCopy& operator=(const NoCopy&) = delete;
};
void onlyInt(int);
template<typename T> void onlyInt(T) = delete; // 阻止其它类型实例调用面试:为什么移动是 noexcept?为什么禁拷贝的类通常也禁赋值?——因为会破坏资源唯一性。default 与 delete 都能被继承影响,尤其=default 让你显式保留默认构造。
四、nullptr vs NULL(为什么 C++11 要 nullptr)
cpp
#define NULL 0 // 历史原因可能是 0 或 (void*)0
// C++ 精确重载痛点:
void f(int);
void f(char*);
f(NULL); // ?? 调用 f(int)(NULL 是 int 0)—— 想调 char* 却调成 int
f(nullptr);// 明确调用 char* 重载 ✅nullptr_t类型,可隐式转任意指针与bool,但不与整型混淆、不能当int 0用;消除指针/整型二义性,更安全。- 适用场景:函数重载、模板推导、比较、智能指针判空等。
五、range-for 与结构化绑定(速记)
cpp
for (const auto& x : vec) // 只读遍历用 const auto&
for (auto x : vec) // 拷贝(值太大浪费)谨慎
for (auto& x : vec) // 修改
for (auto&& x : vec) // 万能,透明(无拷贝)
auto [k, v] = m; // 结构化绑定(C++17)
for (auto& [k,v] : m) { ... } // 遍历 map,且修改 value坑:range-for 底层是升级到迭代器;修改容器导致迭代器失效时对其遍历是 UB(如往 vector 里 push_back 过程中遍历)。