C++ -- 型号比对和constexpr
1、std::is_same
std::is_same是 C++ 标准库<type_traits>头文件中提供的一个类型特征(Type Trait)模板类。它的主要作用是在编译期严格判断两个类型是否完全相同。
std::is_same是一个模板结构体,接受两个类型参数T1和T2。它包含一个静态成员常量value:
- 如果
T1和T2是完全相同的类型,std::is_same<T1, T2>::value为true。 - 否则,为
false。
从 C++17 开始,引入了变量模板std::is_same_v,它是::value的简写形式,使用更加便捷。
#include <iostream> #include <type_traits> int main() { // C++11/14 写法 std::cout << std::boolalpha; std::cout << "int vs int: " << std::is_same<int, int>::value << std::endl; // true std::cout << "int vs double: " << std::is_same<int, double>::value << std::endl; // false // C++17 及以后推荐写法 (更简洁) std::cout << "int vs int (v): " << std::is_same_v<int, int> << std::endl; // true return 0; }std::is_same进行的是字面意义上的严格类型比较,它不会进行任何隐式类型转换或类型归一化。以下情况均被视为不同类型:
// 这些都会返回 false std::is_same_v<int, const int>; // false std::is_same_v<int, int&>; // false std::is_same_v<char, signed char>; // false (注意这点,很多初学者会误以为为 true)2、if constexpr
if constexpr是 C++17 引入的一项关键特性,用于在编译期进行条件判断。它允许编译器根据常量表达式的值,选择性地实例化代码分支。
与传统的运行时if语句或预处理器宏(如#ifdef)不同,if constexpr的核心优势在于:未被选中的分支会被完全丢弃,不参与编译,甚至不会进行语法检查。
if constexpr (condition) { // 当 condition 为 true 时,编译此分支 } else { // 当 condition 为 false 时,编译此分支(可选) }- condition:必须是一个编译期常量表达式(constexpr expression),例如
std::is_integral_v<T>、字面量比较或constexpr变量。 - 行为:
- 如果条件为
true,编译器只实例化if块内的代码。 - 如果条件为
false,编译器只实例化else块内的代码(如果有)。 - 被丢弃的分支被视为“不存在”,即使其中包含语法错误或针对当前类型非法的操作(如调用不存在的成员函数),也不会导致编译错误。
- 如果条件为
template<typename T> void process(T val) { if constexpr (std::is_same_v<T, int>) { std::cout << "处理整数: " << val << std::endl; } else if constexpr (std::is_same_v<T, double>) { std::cout << "处理浮点数: " << val << std::endl; } else { std::cout << "处理其他类型" << std::endl; } }使用std::enable_if控制函数重载,这是 SFINAE 最经典的应用。通过std::enable_if,可以根据类型特征决定是否让某个模板参与重载决议。
SFINAE(Substitution Failure Is Not An Error,替换失败并非错误)机制的技术手段。
#include <iostream> #include <type_traits> // 只有当 T 是整数类型时,此函数才参与重载 template<typename T> typename std::enable_if<std::is_integral<T>::value, void>::type process(T value) { std::cout << "Processing integer: " << value << std::endl; } // 只有当 T 是浮点类型时,此函数才参与重载 template<typename T> typename std::enable_if<std::is_floating_point<T>::value, void>::type process(T value) { std::cout << "Processing float: " << value << std::endl; } int main() { process(42); // 调用整数版本 process(3.14); // 调用浮点版本 // process("hello"); // 编译错误:没有匹配的重载,因为 string 既不是 integral 也不是 floating_point return 0; }typename std::enable_if<std::is_integral<T>::value, void>::type
它的核心作用是:只有当模板参数T是整数类型时,这个表达式才代表一个有效的类型(即void);如果T不是整数类型,该表达式会导致编译时的“替换失败”,从而将当前的函数或类特化从重载候选集中静默移除。
A.std::is_integral<T>::value
- 含义:这是一个类型特征(Type Trait),用于判断类型
T是否为整数类型(如int,char,long,bool等)。 - 结果它是一个编译期常量布尔值:
- 如果
T是整数类型,结果为true。 - 如果
T不是整数类型(如float,double,class等),结果为false。
- 如果
B.std::enable_if<Condition, Type>
- 定义:
std::enable_if是一个模板结构体,定义在<type_traits>头文件中。它接受两个模板参数:B(Condition):一个布尔值。T(Type):一个类型,默认值为void。
- 行为逻辑:
- 如果
B为true:std::enable_if内部会定义一个公共成员类型别名type,其等价于第二个参数T。 - 如果
B为false:std::enable_if内部没有定义任何名为type的成员。
- 如果
C.::type
- 含义:尝试访问
std::enable_if结构体中的成员类型type。 - 关键点:
- 当
std::is_integral<T>::value为true时,::type存在,且等价于void(因为第二个参数传的是void)。 - 当
std::is_integral<T>::value为false时,::type不存在。
- 当
D.typename
- 含义:告诉编译器,后面的
...::type是一个类型名称,而不是静态成员变量或其他东西。因为在模板中,依赖型名称(dependent name)默认不被视为类型,必须显式加上typename。
情况 1:调用myFunction(10),此时T推导为int
std::is_integral<int>::value为true。std::enable_if<true, void>被实例化。- 因为条件为真,
std::enable_if内部定义了typedef void type;。 typename ... ::type成功解析为void。- 函数签名变为
void myFunction(int t)。 - 结果:该函数是一个合法的重载候选,参与编译。
