如何对传递给可变参数宏的可变参数求和

How to sum variadic arguments passed in to a variadic macro?

本文关键字:变参 参数 求和      更新时间:2023-10-16

我想要一个程序来定义一个宏,该宏可以计算参数的数量并将它们传递给函数sum该函数对参数的值求和并返回总数。我设法在 GCC 上做到了这一点,但我想在 Visual C++ 14 上实现它。

#include "stdafx.h"
#include <iostream>
#include <cstdarg>

#define ELEVENTH_ARGUMENT(a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, ...) a11
#define COUNT_ARGUMENTS(...) ELEVENTH_ARGUMENT(dummy, ## __VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define SUM(...) sum(ELEVENTH_ARGUMENT(dummy, ## __VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))

int sum(int n, ...) {
    int sz{ n };
    va_list ap;
    va_start(ap, n);
    int tmp{};
    while (--sz)
        tmp += va_arg(ap, int);
    va_end(ap);
    return tmp;
}
int main() {
    std::cout << COUNT_ARGUMENTS(4,57,22,10,5,6,2,8,68,24,24,86,89,89,96,86) << std::endl; // 1
    std::cout << SUM(5, 57, 4, 5) << std::endl; // 0
    std::cout << COUNT_ARGUMENTS(5, 57, 10) << std::endl;// 1

    std::cout << std::endl;
    std::cin.get();
}

我不知道我的代码出了什么问题,它总是给我总和是 0。

不要使用可变参数宏。 Visual C++ 14(或 2015)是符合 C++11/14 的编译器。 这意味着它支持可变参数模板。 您可以轻松递归参数包以获取参数的总和,并且可以使用 sizeof... 完成计数。 这使您可以将count写为

template<typename... Args>
auto count(Args&&...)
{
    return sizeof...(Args);
}

然后sum可以写成

// base case
template<typename T>
auto sum(T&& first)
{
    return first;
}
// multiple parameters case
template<typename T, typename... Args>
auto sum(T&& first, Args&&... rest)
{
    return first + sum(rest...);
}

int main()
{
    std::cout << count(3,4,5) << "n";
    std::cout << sum(3,4,5);
}

指纹

3
12

你可以在这个现场的例子中看到。


正如HolyBlackCat所建议的那样,您可以使用虚拟数组技巧来避免使用递归。 那会给你sum一个看起来像

template <typename ...P> 
auto sum(const P &... params)
{
    using dummy_array = int[];
    std::common_type_t<P...> ret{}; // common_type_t is to find the appropriate type all of the parameter can be added to
    (void)dummy_array{(void(ret += params), 0)..., 0}; // add the parameter to ret, discard it's result, return the value of 0 for the array element
    return ret;
}

请注意,这可能不适用于所有类型,如此处所示 std::valarray . 将其更改为

template <typename T, typename ...P> 
auto sum(T first, P&&... rest) // copy the first parameter to use it as the accumulator
{
    using dummy_array = int[];
    (void)dummy_array{(void(first += params), 0)..., 0}; // add the parameter to ret, discard it's result, return the value of 0 for the array element
    return first;
}

应该更正确,尽管它可能会进一步改进(欢迎建议/编辑)


如果您可以使用 C++17 投诉编译器,那么可以使用折叠表达式进一步简化sum例如

template<typename... Args>
auto sum(Args&&... rest)
{
    return (rest + ...);
}

补充@NathanOliver,如果你想使用没有递归的可变参数模板,std::initializer_liststd::common_type在 C++11 中都可用,允许你这样做:

template <typename... Args,                                      
          typename T = typename std::common_type<Args...>::type> 
T sum(Args&&... args) {                                          
    std::initializer_list<T> l{args...};                         
    return std::accumulate(l.begin(), l.end(), T{});             
}                                                                

编辑:请注意,此解决方案将允许在可隐式转换为通用类型的所有类型上调用 sum(这就是common_type所做的)。

例如:

sum(1, 2, 3)    // Ok, all ints
sum(1, 2, true) // Ok, bool converts to int
sum(1, 2, 3.)   // Error, int to double is a narrowing conversion