跳转到内容

什么是 if constexpr 语句?为什么要引入?

看代码:

#include <iostream>
void func(const std::string& param) {
if (false) {
std::cout << param * 2 << std::endl;
}
}
int main() {
std::string n{"nice"};
func(n);
}

编译上述代码会报错,因为 std::string 没有实现 * 运算符。

可是我都把相关语句放到 if (false) 里了,压根就不会执行,为什么还会报错呢?

因为对条件的判断是在程序运行时进行,而对语法的检查是在编译时进行,编译时通不过,那就更遑论运行了。

听说,使用if constexpr语句可以在编译时进行条件判断,如果条件分支为 false 则该分支的代码将从函数中删除。使用if constexpr语句,那么还有上述问题吗?

#include <iostream>
void func(const std::string& param) {
if constexpr (false) {
std::cout << param * 2 << std::endl;
}
}
int main() {
std::string n{"nice"};
func(n);
}

依然有,会有相同的报错。为什么?因为上面的函数func不是模板函数。cppreference 原文:

Outside a template, a discarded statement is fully checked. if constexpr is not a substitute for the #if preprocessing directive

如果非模板,被丢弃的语句会被完全检查。if constexpr 并不是 #if 预处理指令的替代品。 *

改为下面这样,程序就可以正常运行了:

#include <iostream>
template <typename T>
void func(const T& param) {
if constexpr (false) {
std::cout << param * 2 << std::endl;
}
}
int main() {
std::string n{"nice"};
func(n);
}

但这样的程序除了演示语法之外并没有什么用。改写程序让它有点用吧:

/*
* 如果传递给 func() 的参数类型为数值,则将参数乘以2后打印出来,否则直接将参数打印出来。
*/
#include <iostream>
template <typename T>
void func(const T& param) {
if constexpr (std::is_arithmetic_v<T>) {
std::cout << param * 2 << std::endl;
} else {
std::cout << param << std::endl;
}
}
int main() {
int m {21};
func(m);
std::string n{"nice"};
func(n);
}

例子 使用 if constexpr 代替模板专门化

更新 [[2024-06-24]] 14:26

想使用函数模板render()渲染多个类,打印它们的数据成员。

初始代码如下:

#include <iostream>
struct Tree {
std::string description{"Tree"};
};
struct Rock {
std::string description{"Rock"};
};
struct Monster {
std::string name{"Monster"};
};
template <typename T>
void render(const T& object) {
std::cout << "rendering a "
<< object.description << '\n';
}
int main() {
render(Tree{});
render(Rock{});
render(Monster{}); // error
}

Monster类与其他类不同,在渲染到它的时候会编译出错,因为它的成员变量不是description而是name。一种解决办法是添加函数模板render()针对该Monster类的专门化(specialization),代码如下:

template <>
void render<Monster>(const Monster& object) {
std::cout << "rendering a "
<< object.name << '\n';
}

函数模板专门化会让代码变长,可能产生代码冗余。

对于上面的例子,另一种解决办法是使用 if constexpr 语句 和 类型特征(type traits)。修改函数模板render()

template <typename T>
void render(const T &object) {
if constexpr (std::is_same_v<T, Monster>) {
std::cout << "rendering a " << object.name << '\n';
} else {
std::cout << "rendering a " << object.description << '\n';
}
}

对于只有很少几处代码有变化的程序,用第二种方法看起来更合适。

参考