初始化与推断
创建对象时,初始值必须有明确来源。初始化发生在变量诞生时,赋值发生在变量已经存在之后。auto 则让编译器从初始化表达式推断类型。本章使用 C++11 引入的列表初始化和类型推断,并按 C++20 的规则观察结果。
初始化与赋值
优先使用花括号初始化。空花括号会把基础类型初始化为零或对应的值,也能拒绝许多窄化转换:
片段:初始化与赋值
// 片段
int count{}; // 值初始化为 0
int limit{100}; // 创建时得到 100
count = 80; // 修改已有变量
// int level{3.5}; // 编译失败:窄化转换
int truncated = 3.5; // 可编译,但结果为 3,不推荐局部自动变量若只写 int count; 就直接读取,其值是不确定的,这类读取属于未定义行为。不要为了“看看会输出什么”而运行这种代码。
类型推断
auto 不是动态类型。编译器在编译期根据初始化表达式确定类型,变量创建后仍保持该类型:
initialization-and-type-inference.cpp
#include <iostream>
int main() {
int count{};
int limit{100};
count = 80;
auto value = 1200; // int
auto ratio = 4.5; // double
auto enabled = true; // bool
value -= 100;
std::cout << "数量: " << count << "/" << limit << "\n";
std::cout << "值: " << value << "\n";
std::cout << "比例: " << ratio << "\n";
std::cout << std::boolalpha << "已启用: " << enabled << "\n";
return 0;
}当右侧类型一眼可见时,auto 能减少重复,当类型本身就是接口意图时,显式写出类型更清楚。
列表推导
C++11 的 auto 与花括号组合有特殊规则。C++20 中,多个元素会推导为 std::initializer_list<T>,单个元素的直接列表初始化推导为该元素类型:
片段:初始化列表推导
// 片段
auto one_value{10}; // int
auto many_values{10, 20, 30}; // std::initializer_list<int>
auto same_type = {10, 20}; // std::initializer_list<int>
// auto mixed{10, 2.5}; // 编译失败:元素类型无法统一auto 仍然需要初始化表达式。auto value; 没有推断来源,会编译失败。
常见错误
初始化当赋值
初始化只发生一次,之后的 = 是赋值,区分二者能避免误判对象何时获得第一个有效值。
漏掉窄化检查
列表初始化拒绝可能丢失信息的转换,遇到编译错误时先确认目标类型和数值范围,而不是马上改回 C 风格强转。
误以为 auto 会改变类型
auto 只在编译期推断一次,不会让变量在运行时从 int 变成 double。
自测问题
count{}和count = 80分别发生在什么时机?auto many_values{10, 20, 30};为什么不是一个int?- 什么情况下显式类型比
auto更能表达接口意图?
本章回顾
你已经能用列表初始化建立安全的初始值,并能解释 C++11 auto 与初始化列表的推导边界。示例见 initialization-and-type-inference.cpp。