C++17 之 "constexpr if"

普通的 if-else 是在执行期进行条件判断与选择, 这意味着在泛型编程中无法使用
if-else 语句进行条件判断. 比如例 1 会引起编译错误

例 1: 将多个数累加并返回累加结果

template <int N, int... Ns>
auto sum()
{
    if (sizeof...(Ns) == 0) // 若参数包为空, 直接返回 N
        return N;
    else                    // 否则进行递归调用
        return N + sum<Ns...>();
}

// 调用
sum<1, 2, 3>();

例 1 无法编译通过.

C++17 之前的做法

C++17 之前, 必须利用多个模板才能完成例 1 的功能

例 2: C++17 之前, 将多个数累加并返回累加结果

// 只有一个模板参数时调用此模板
template<int N>
int sum()
{
    return N;
}

// 模板参数 > 2 个时调用此模板
template <int N, int N2, int... Ns>
int sum()
{
    return N + sum<N2, Ns...>();
}

// 调用
sum<1, 2, 3>(); // returns 6

C++17 的作法

C++17 引入 constexpr if 支持在编译期执行, 可以将之应用于泛型编程中的条件判断,
如例 3.

例 3: C++17, 将多个数累加并返回累加结果

//
template <int N, int... Ns>
auto sum()
{
    if constexpr (0 == sizeof...(Ns))
        return N;
    else
        return N + sum<Ns...>();
}

// 调用
sum<1, 2, 3>(); // returns 6

更简单的是利用 C++17 提供的新功能 “折叠表达式”, 如例 4.

例 4: 利用折叠表达式实现多个数累加并返回累加结果

//
template<typename ...Ns>
auto sum(Ns... ns) {
    return (ns + ...);
}

// 调用
sum(1, 2, 3); // returns 6

更多

constexpr if 可以应用更多的场合, 比如要将数值转化为字符串. 在 C++17
之前, 需要使用 std::enable_if 来判断参数类型, 如例 4.

例 4

// 数值转换
template<typename T>
std::enable_if_t<std::is_integral<T>::value, std::string>
to_string(T t)
{
    return std::to_string(t);
}

template<typename T>
std::enable_if_t<!std::is_integral<T>::value, std::string>
to_string(T t)
{
    return t;
}

而在 C++17 中可以利用 constexpr if 完成相同功能, 且摒弃了冗长的
std::enable_if 语句, 不仅代码可读性好, 而且书写也更方便, 如例 5.

例 5

template<typename T>
auto to_string(T t)
{
    if constexpr(std::is_integral<T>::value)
        return std::to_string(t);
    else
        return t;
}

便是要注意在 constexpr if 中的 return 语句, 下面的写法是不对的,
应当在条件语句的分支中 return, .

template<typename T>
auto to_string(T t)
{
    if constexpr(std::is_integral<T>::value)
        return std::to_string(t);
    // 此处需要 else
    return t;
}

最后

正如许多 C++ 著作中提到的, 如果一个函数中使用了大量的 if-else 语句,
那么需要重构, 将多个条件语句转化成函数重载会更加清晰. 同理,
如果泛型函数中使用大量的 constexpr if 语句, 也许也需要考虑代码重构.

猜你喜欢

转载自blog.csdn.net/ding_yingzi/article/details/79977747