如何编写接受无限参数的函数?

Posted

技术标签:

【中文标题】如何编写接受无限参数的函数?【英文标题】:How do I write functions that accept unlimited arguments? 【发布时间】:2012-02-20 20:59:15 【问题描述】:

我只能找到一种方法让函数接受可变数量的参数。 是这样的:

#include <iostream>
#include <stdarg.h>

using namespace std;

void Print(int argumentAmount, ... );

int main()

    Print(5,11,22,33,44,55);


void Print(int argumentAmount, ... )

    va_list arguments; 
    va_start(arguments, argumentAmount);

    int parameter;
    for(int i = 0; i < argumentAmount; ++i )
        parameter = va_arg(arguments, int);
        cout << parameter << endl;
    

    va_end(arguments);
    return;

2 个问题:1.)我必须指定我要发送多少个参数 - 不可取2.) 我不知道如何修改它,所以它会输出字符串。

如果不必多次重载函数,这样的事情是否可行:

void Output(/*not sure how this would look*/);

int main()

    Output("hello","world");
    Output("this","is","a","test");
    Output("As","many","strings","as","you","want","may","be","passed","in");

    return 0;

void Output(/*not sure how this would look*/)

    //loop through each string passed in and output it

这个呢:

void Capitalize(/*all passed by reference*/);

int main()

    string s1 = "hello";
    string s2 = "world";

    string s3 = "this";
    string s4 = "is";
    string s5 = "a";
    string s6 = "test";

    string s7 = "as";
    string s8 = "many";
    string s9 = "strings";
    string s10 = "as";
    string s11 = "you";
    string s12 = "want";

    Capitalize(s1,s2);
    Capitalize(s3,s4,s5,s6);
    Capitalize(s7,s8,s9,s10,s11,s12);

    return 0;

void Capitalize(/*all passed by reference*/)

    //capitalize each string passed in


我能想到的只有: - 多次重载函数 - 让函数接受某种类型的容器

如果这是不可能,有人可以解释为什么编译器无法完成这样的任务。

【问题讨论】:

呃,您在顶部提供的解决方案是对底部提出的两个问题的答案。 “修改它使其输出字符串”是什么意思? 那么,你为什么不直接在数组或某种集合中传递字符串呢? 自从我查看 C 以来已经有一段时间了 - 但一个快速的谷歌发现了这个:[weblogs.asp.net/whaggard/archive/2004/07/03/172616.aspx] 很好地说明了语法。 编译器的唯一工作就是编译 C++ 代码。您是在问为什么 C++ 语言不支持该语法? 顺便说一句,“无限参数”有点陡峭;我今天才发现,C 编译器不需要支持超过 127 个参数! 【参考方案1】:

我认为还有另一种可能的解决方案: 您可以像这样重载运算符'

class OutputObject 
public:
    // Some class functions/members

;
template<class T>
static operator << (OutputObject& out, T temp) 
    cout << temp;

static OutputObject Obj = OutputObject();

然后你可以在main中执行以下操作:

#include "OutputObject.hpp"
#include <string>
using namespace std;
int main(void) 
    string str = "Hello World";
    Obj << 12 << str << 3.14f << "C++";
    Obj << 12;
    Obj << str;
    return(0);
;

如果我做错了什么或有理由不这样做,请 告诉我,那只是我的无限参数的想法。 我还不能测试它,但我认为它应该可以工作。

【讨论】:

为了将来参考,您应该推迟发布,直到您测试并验证了您的声明。 对不起,我的错。实际上,我对其进行了测试,但有一个例外:您不能在一次调用中传递多个类,而是在多个类中传递: Obj 它应该可以工作,但很难说你做错了什么,因为你的代码 sn-p 在语法上不正确;你错过了返回类型。大概你给了它void。不要那样做【参考方案2】:

使用 C++11 中的可变参数模板,您可以执行以下操作(请参阅 the result at ideone)

#include <string>
#include <iostream>

void Output() 
    std::cout<<std::endl;


template<typename First, typename ... Strings>
void Output(First arg, const Strings&... rest) 
    std::cout<<arg<<" ";
    Output(rest...);


int main() 
    Output("I","am","a","sentence");
    Output("Let's","try",1,"or",2,"digits");
    return 0;

【讨论】:

哇,我想你只是帮助我理解了可变参数模板。 等等,main 中的第二次调用实际上不会在内存中创建 5 个不同的函数,还是实际上是递归的? 是的,对 Output 的每次调用都有一组不同的模板参数,因此会导致不同的模板实例化。编译器实际上是在“内存中”创建所有这些实例化,还是内联到调用者中取决于函数代码、优化级别等。【参考方案3】:

您可以使用特殊的“尾随”参数(nullptr 或指向某些硬编码“魔术”字符串的指针)而不是传入计数,并且您的变量参数函数应该停止提取更多参数见尾随。这可以稍微简化您的编码。

您还可以将指针(引用)传递给包含(或指向/引用)您的字符串的容器。任何可以以某种方式链接您所有个人参数的东西都可以(例如向量)。

示例(可能不是很惯用,但应该用作说明):

#include <iostream>
#include <string>
#include <cstdarg>
#include <cctype>
#include <vector>

using namespace std;

void AntiCapitalize(vector<string*>& v);
void Capitalize(string* s, ...);
void Print(string* s, ...);

int main()

    string s1 = "hello";
    string s2 = "world";

    string s3 = "this";
    string s4 = "is";
    string s5 = "a";
    string s6 = "test";

    string s7 = "as";
    string s8 = "many";
    string s9 = "strings";
    string s10 = "as";
    string s11 = "you";
    string s12 = "want";

    Capitalize(&s1, &s2, 0);
    Capitalize(&s3, &s4, &s5, &s6, 0);
    Capitalize(&s7, &s8, &s9, &s10, &s11, &s12, 0);

    Print(&s1, &s2, 0);
    Print(&s3, &s4, &s5, &s6, 0);
    Print(&s7, &s8, &s9, &s10, &s11, &s12, 0);

    vector<string*> v;
    v.push_back(&s1);
    v.push_back(&s2);
    v.push_back(&s3);
    v.push_back(&s4);
    v.push_back(&s5);
    v.push_back(&s6);
    v.push_back(&s7);
    v.push_back(&s8);
    v.push_back(&s9);
    v.push_back(&s10);
    v.push_back(&s11);
    v.push_back(&s12);

    AntiCapitalize(v);

    Print(&s1, &s2, 0);
    Print(&s3, &s4, &s5, &s6, 0);
    Print(&s7, &s8, &s9, &s10, &s11, &s12, 0);

    return 0;


void Capitalize(string* s, ...)

    va_list ap;

    va_start(ap, s);

    while (s)
    
        string::size_type i = 0;

        while ((*s)[i] != '\0')
        
            (*s)[i] = toupper((*s)[i]);
            i++;
        

        s = va_arg(ap, string*);
    

    va_end(ap);


void Print(string* s, ...)

    va_list ap;

    va_start(ap, s);

    while (s)
    
        cout << *s << endl;
        s = va_arg(ap, string*);
    

    va_end(ap);


void AntiCapitalize(vector<string*>& v)

    vector<string*>::iterator it;

    for (it = v.begin(); it != v.end(); it++)
    
        string::size_type i = 0;

        while ((**it)[i] != '\0')
        
            (**it)[i] = tolower((**it)[i]);
            i++;
        
    

输出:

HELLO
WORLD
THIS
IS
A
TEST
AS
MANY
STRINGS
AS
YOU
WANT
hello
world
this
is
a
test
as
many
strings
as
you
want

【讨论】:

【参考方案4】:

快速简单的回答。

对于 C++,您需要指定参数的数量或标记值来指示参数的结束。

您的第一个示例是指定计数的一个很好的示例,您也可以这样做:

void Print(const char *arg, ... )
    va_list arguments;

    for (va_start(arguments, arg); arg != NULL; arg = va_arg(arguments, const char *)) 
        cout << arg << endl;
    

    va_end(arguments);

您的调用约定在哪里:

Print("foo","bar",NULL);

如果你想把它提升到一个新的水平,你可以混合一点 C 预处理器然后做:

#define mPrint(...) Print(__VA_ARGS__, NULL)

现在你可以说:

mPrint("fooo","bar");

宏将NULL 终止调用。

【讨论】:

以上是关于如何编写接受无限参数的函数?的主要内容,如果未能解决你的问题,请参考以下文章

具有无限参数的 Java 方法

如何编写没有输入参数的 FreeMarker 模板函数

PHP 函数可以接受无限数量的参数吗? [复制]

参数数量不限的 PHP 函数

如何使用接受多列作为参数的java为hive编写UDAF?

如何创建一个从管道和命令行接受多种参数类型的函数?