0

I want to calculate the sum of any number of arguments given to function sum. Assuming that integers given to the function will satisfy operator+.

If I comment out the function sum() (the one having no arguments), the code does not compile. And if I uncomment it, the code does compile and run, but never hits the function sum().

I can't seem to understand that why do we need to have sum() function at all as I am using condition on sizeof...(Args)

Will really appreciate it if someone can help me understand this?

/*
int sum() 
{
    std::cout << "Sum with 0 Args" << std::endl;
    return 0;
}
*/

template <typename T, typename...Args>
T sum(T first, Args...args) 
{
    // std::cout << sizeof...(Args) << std::endl;
    if (sizeof...(Args) != 0) 
    {
        return first + sum(args...);
    }
    else 
    {
        std::cout << "Found 0 args" << std::endl;
        return first;
    }
}

int main()
{
    std::cout << sum(1, 2, 3) << std::endl;
    std::cout << sum(1.2, 3.5) << std::endl;
    return 0;
}

Once I uncomment function sum(), I get below output -

Found 0 args
6
Found 0 args
4.7

Basically sum() never get called which is expected, but then why do we need it in the first place?

JeJo
  • 30,635
  • 6
  • 49
  • 88
  • 3
    Do you know what `if constexpr` is, why it's needed, how it works, and how to use it? In C++, even if something "never get called", it still must be valid C++. Unless there's `if constexpr` involved. – Sam Varshavchik Jul 31 '23 at 15:22
  • 2
    Why do you pick two C++ versions? Do you use them both in a single code? – 273K Jul 31 '23 at 15:26

3 Answers3

5

Basically sum() never get called which is expected but then why do we need it in the first place?

When you use the normal if statement, like in your code, the both branches are checked, and both must be compilable. When the sum is called recursively, the last function call is with no arguments. For this case to be true, the compiler requires a sum function with no arguments.

On the other hand, since , we have if constexpr, by which we can retain only the true branches at compile time. That means, just changing the code as follows, you no longer required the sum() with no argument anymore.

Read more here: Difference between if constexpr and if?

template <typename T, typename...Args>
T sum(T first, Args...args) 
{
    if constexpr (sizeof...(Args) != 0) 
    // ^^^^^^^^^^
    {
       // .... as before
    }
    else {
       // .... as before
    }
}

However, in or , you may achieve a single sum function, by slightly tricky/ hacky fold expression:

#include <type_traits> // std::common_type

template <typename...Args>
auto sum(Args...args) -> typename std::common_type<Args...>::type
{
    using unused = int[];
    typename std::common_type<Args...>::type  total{};
    return static_cast<void>(unused{ 0, (total += args, 0)...}), total;
}

See a live demo in godbolt.org


References:

JeJo
  • 30,635
  • 6
  • 49
  • 88
0

From C++17 you can use a fold expression for you recursive bit, and you have to use if constexpr for compile time if.

#include <iostream>

template<typename... args_t>
auto sum(args_t&&... args)
{
    if constexpr (sizeof...(args_t) == 0)
    {
        return 0;
    }
    else
    {
        return (args + ...);
    }
}

int main()
{
    std::cout << sum() << "\n";
    std::cout << sum(1, 2, 3);
    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19
  • OP picked C++14 that doesn't have if constexpr. – 273K Jul 31 '23 at 15:30
  • You can also `return (0 + ... + args);`, so you don't need the special case for zero arguments :-) I think the anwer is still valid even though it's C++17 for anyone having the same problem who might not be restricted to C++14. – chrysante Jul 31 '23 at 15:31
  • @chrysante There is a bunch of such questions with answers for C++17, thus one more such C++17 answer is not needed when C++14 has been asked. – 273K Jul 31 '23 at 15:35
  • @chrysante thanks for that little tip... somehow I always limited my fold expressions two to parts ;) – Pepijn Kramer Jul 31 '23 at 16:09
0

I missed the C++11 requirement earlier. Here is an example that should be C++ compatible. Demo here : https://onlinegdb.com/js_WXEeiC

#include <iostream>

namespace details
{
    template<typename arg1_t>
    inline constexpr bool same_type()
    {
        return true;
    }

    template<typename arg1_t, typename arg2_t, typename... args_t>
    inline constexpr bool same_type()
    {
        return std::is_same<arg1_t, arg2_t>::value && same_type<arg2_t, args_t...>();
    }

    template<typename arg_t>
    arg_t sum(arg_t value)
    {
        return value;
    }


    template<typename arg_t, typename... args_t>
    arg_t sum(arg_t value, args_t... values)
    {
        return value + sum(std::forward<args_t>(values)...);
    }
}

template<typename arg_t, typename... args_t>
auto sum(arg_t value, args_t... values)
-> typename std::enable_if<details::same_type<arg_t, args_t...>(), arg_t>::type
{
    return details::sum(value, values...);
}

int main()
{
    static_assert(details::same_type<int>());
    static_assert(details::same_type<int, int>());
    static_assert(details::same_type<int, int, int>());
    static_assert(!details::same_type<int, int, double>());


    std::cout << sum(1, 2, 3);
    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19