10 类型转换、类型别名、类型推导
10.4 窄化转换(收缩转换)、列表初始化和constexpr初始化器
- 窄化转换是一种不安全的数值转换:目标类型可能无法保存源类型的所有值
- 以下转换是窄化的:
- 浮点型转换成整型
- 从浮点型转换成等级更低的浮点型(除非被转换的值是constexpr并且在目标类型的范围内)
- 从整型转换成浮点型(除非要转换的值是constexpr并且值可以精确地存储在目标类型中)
- 从一个整型转换成另一个不能表示源类型的所有值的整型(除非要转换的值是constexpr并且其值可以精确地存储在目标类型中)(包括两种情况:宽整型到窄整型的转换、有符号整数转换成到无符号整数或者反过来)
- 避免使用窄化转换
- 使用
static_cast
来显式执行窄化转换可以避免编译器的警告 - 使用大括号初始化时不允许窄化转换(这也是大括号初始化的主要优点之一),否则会产生编译错误
- 如果窄化转换的源值是constexpr,那么编译器已经知道了要被转换的值,也就知道了这个值是否会在转换后被保留(如果会被保留则认为这不是窄化转换,如果不会被保留则编译报错)
const int i{128};
char c{i}; // 如果i的值在-128到127之间,则编译通过;否则编译报错:Constant expression evaluates to 128 which cannot be narrowed to type 'char'
// 注意!!!这里编译报错是因为大括号初始化不允许窄化转换;如果使用的是=初始化c,则编译器只会警告出现了窄化转换,而不会报错
- 从constexpr浮点型转换成更窄的浮点型不是窄化转换,即使损失了精度
constexpr double d{0.1};
float f {d};// compile ok
- 当使用列表初始化来初始化非int/非double的对象时,选择constexpr表达式作为初始化器可以避免窄化转换
10.6 显式类型转换和static_cast
- C++提供了5种类型转换:C风格转换、静态转换、常量转换、动态转换、重新解释转换,后四种类型也称为命名转换
尽量避免使用常量转换和重新解释转换
- C风格转换的形式是
(type_name)variable_name
或者type_name(variable_name)
尽管C风格转换看起来是单一的转换,但实际上它可以根据上下文执行各种不同的转换,包括静态转换、常量转换和重新解释转换
因此,尽量避免使用C风格转换
static_cast
是一个运算符,它接收一个表达式作为输入,然后计算得到一个指定类型的值。最适合用于将一种基本类型转换为另一种基本类型
static_cast的主要优点是提供了编译时的类型检查
- 使用
static_cast
可以显式进行窄化转换 - 隐式类型转换和显式类型转换的区别
每当需要一种数据类型而提供了另一种数据类型时,都会自动执行隐式类型转换
而显式类型转换是程序员显式地使用类型转换运算符将一种类型转换成另一种类型
10.8 使用auto进行对象的类型推导
- 类型推导是一个特性:允许编译器根据对象的初始化器来推断对象的类型
- 字面量后缀可以和auto结合使用
auto a{1.2f}; // 变量a被推断为float类型
auto b{5u}; // 变量b被推断为unsigned int类型
- 如果对象没有初始化器或者初始化器为空/void,则类型推断无效,以下三条语句都会编译报错
auto x;
auto y{};
auto z{f()}; // f()的返回类型为void
- 类型推断会丢弃const/constexpr限定符,除非手动添加const/constexpr
const int x{5}; // x的类型是const int
auto y{x}; // y的类型是int
const auto z{x}; //z的类型是const int
- 字符串字面量的类型推断结果为
const char*
,而不是std::string
/std::string_view
,除非在字符串字面量后面加上后缀s
/sv
- 类型推断的优点
- 使用类型推断时,变量必须有初始化器,因此可以避免忘记初始化变量
- 避免出现影响性能的转换
- 类型推断的缺点
- 类型推断使得变量的类型信息不那么直观
- 使用类型推断时,当变量的初始化器的类型改变后,变量的类型也会改变
- 当对象的类型不重要时,对变量使用类型推断