web-dev-qa-db-ja.com

無制限の引数を受け入れる関数を作成するにはどうすればよいですか?

私は関数が可変量の引数を取るためのone方法を見つけることができました。
それはこのようです:

#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

}

私ができると考えることができるすべては:
-関数を複数回オーバーロードします
-関数に代わりにあるタイプのコンテナーを受け入れさせる

これがNOT POSSIBLEの場合、コンパイラがこのようなタスクを実行できない理由を誰かが説明できますか?.

20
Trevor Hickey

C++ 11の可変個のテンプレートを使用すると、次のようなことができます(- 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;
}
41
Alexey Kukanov

すばやく簡単な答え。

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で呼び出しを終了します。

12
koblas

カウントを渡す代わりに、特別な「末尾」引数(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
6
Alexey Frunze

別の可能な解決策があると思います:次のように演算子 '<<'をオーバーロードできます:

class OutputObject {
public:
    // Some class functions/members

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

そして、あなたはメインで以下を行うことができます:

#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);
};

私が何か間違ったことをしたり、それができない理由がある場合は、教えてください。それは、無限のパラメータに関する私の考えでした。まだテストはできませんでしたが、うまくいくと思います。

2
Lars