C++ if constexpr的用法(非常详细)
C++ 中,当使用 SFINAE 和 std::enable_if 来定义函数重载或编写可变参数函数模板时,模板元编程可能变得复杂和混乱。C++17 的一个特性就是为了简化这类代码而提出的,它被称为 if constexpr,它定义了一个 if 语句,该语句的条件在编译时进行计算,导致编译器选择编译单元中的分支或其他分支的主体。
if constexpr 的典型用法是简化可变参数模板和 std::enable_if 的代码。
下面我们将探讨使用 if constexpr 进行条件编译的几个用例。使用 if constexpr 语句完成以下操作:
1) 避免使用 std::enable_if 和依赖 SFINAE 对函数模板类型施加限制并有条件地编译代码:
2) 简化可变参数模板的编写,实现编译时递归元编程:
然而,如果没有 if constexpr 的帮助,我们只能使用 std::enable_if 来实现它。下面的实现是一个更糟糕的替代方案:
if constexpr 的典型用法是简化可变参数模板和 std::enable_if 的代码。
C++ if constexpr的使用方式
if constexpr 的语法与常规的 if 语句非常类似,并且要求在条件之前使用 constexpr 关键字。一般形式如下:if constexpr (init-statement condition) statement-true else statement-falseif 语句中的条件必须是一个编译时表达式,该表达式的计算值为布尔值或可转换为布尔值。如果条件为 true,则选择 if 语句的主体,这意味着它将在编译单元中结束;如果条件为 false,则计算 else 分支(如果定义了分支)。
下面我们将探讨使用 if constexpr 进行条件编译的几个用例。使用 if constexpr 语句完成以下操作:
1) 避免使用 std::enable_if 和依赖 SFINAE 对函数模板类型施加限制并有条件地编译代码:
template <typename T> auto value_of(T value) { if constexpr (std::is_pointer_v<T>) return *value; else return value; }
2) 简化可变参数模板的编写,实现编译时递归元编程:
namespace binary { using byte8 = unsigned char; namespace binary_literals { namespace binary_literals_internals { template <typename CharT, char d, char... bits> constexpr CharT binary_eval() { if constexpr(sizeof...(bits) == 0) return static_cast<CharT>(d-'0'); else if constexpr(d == '0') return binary_eval<CharT, bits...>(); else if constexpr(d == '1') return static_cast<CharT>( (1 << sizeof...(bits)) | binary_eval<CharT, bits...>()); } } template<char... bits> constexpr byte8 operator"""_b8() { static_assert( sizeof...(bits) <= 8, "binary literal b8 must be up to 8 digits long"); return binary_literals_internals:: binary_eval<byte8, bits...>(); } } }
C++ if constexpr的工作原理
在前文的第一个例子中,value_of() 函数模板有一个整洁的签名,函数体也很简单。如果替换模板形参的类型是指针类型,编译器将选择第一个分支(即 return*value;)来生成代码,并丢弃 else 分支。对于非指针类型,因为条件的计算结果为 false,编译器将选择 else 分支(即 return value;)来生成代码,并丢弃其余部分。该函数的使用方法如下:auto v1 = value_of(42); auto p = std::make_unique<int>(42); auto v2 = value_of(p.get());
然而,如果没有 if constexpr 的帮助,我们只能使用 std::enable_if 来实现它。下面的实现是一个更糟糕的替代方案:
template <typename T, typename = typename std::enable_if_t<std::is_pointer_v<T>, T>> auto value_of(T value) { return *value; } template <typename T, typename = typename std::enable_if_t<!std::is_pointer_v<T>, T>> T value_of(T value) { return value; }如你所见,if constexpr 变体不仅更短,而且更有表现力,更容易阅读和理解。