How to use if constexpr in template fold expressions?
Asked Answered
G

2

8

I would like to write a sum function with variable number of argument with the condition that it should ignore argument that are not std::is_arithmetic

I have figured out a recursive version that works

auto old_sum(){
    return 0;
}

template<typename T1, typename... T>
auto old_sum(T1 s, T... ts){
    if constexpr(std::is_arithmetic_v<T1>)
        return s + old_sum(ts...);
    else
        return old_sum(ts...);
}

I am wondering if I can use the if constexpr in the context of fold expression to make the following code only considers arithmetic types from the argument pack:

template<typename... T>
auto fold_sum(T... s){
    return (... + s);
}
Geostrophic answered 14/12, 2018 at 17:18 Comment(0)
N
12

As we do not have a ternary constexpr operator, we can use a lambda instead.

#include <type_traits>

template<typename... T>
constexpr auto fold_sum(T... s){
    return (... + [](auto x)
    {
        if constexpr(std::is_arithmetic_v<T>) return x;
        else return 0;
    }(s));
}

Usage:

int main()
{
    static_assert(fold_sum(0, nullptr, 5, nullptr, 11, nullptr) == 16);
}

live example on godbolt.org

Nosey answered 14/12, 2018 at 17:28 Comment(4)
You should give the fold the identity of the sum to handle empty pack.Phlogistic
Would be quite a bit easier to read if you named the lambda ;-)Catabasis
Is the else really necessary here? I can't imagine if constexpr would allow for a way to write unreachable code if intentionally misused.Lobell
Yes else is necessary. This is a limitation of if constexpr. The alternative branch must be explicitly marked by else to force ignoring the invalid branch completely. It is not a big deal in most cases because the compiler will be able to optimize out the unless branch, but it would avoid triggering compilation failure if one of the branch does not make sense at compile-time, ie if constexpr (!std::is_same_v<T, std::string>) return x else return x.c_str();. Here, compilation would fail without explicit else directive if c_str is not defined no matter if the condition is satisfied.Gilda
K
4

You absolutely want to use if constexpr?

I propose a different alternative: std::get() and std::pair to simulate a constexpr ternary operator as follows (with a Vittorio Romeo's improvement; thanks)

#include <utility>
#include <type_traits>

template<typename ... Ts>
constexpr auto fold_sum (Ts const & ... s)
 { return (... + std::get<std::is_arithmetic_v<Ts>>(std::pair{0, s})); }

int main ()
 {
   static_assert(fold_sum(0, nullptr, 5, nullptr, 11, nullptr) == 16);
 }
Kenya answered 14/12, 2018 at 18:21 Comment(2)
Can simplify to std::pair(0, s) by relying on CTAD.Nosey
@VittorioRomeo - Good idea; I'm still not used to C++17 argument deduction. Thanks.Kenya

© 2022 - 2024 — McMap. All rights reserved.