web-dev-qa-db-ja.com

C ++の関数内のパラメーターの可変数

C++の関数で可変数のパラメーターを使用する方法。

C#のアナログ:

public void Foo(params int[] a) {
    for (int i = 0; i < a.Length; i++)
        Console.WriteLine(a[i]);
}

public void UseFoo() {
    Foo();
    Foo(1);
    Foo(1, 2);
}

Javaのアナログ:

public void Foo(int... a) {
    for (int i = 0; i < a.length; i++)
        System.out.println(a[i]);
}

public void UseFoo() {
    Foo();
    Foo(1);
    Foo(2);
}
31
AndreyAkinshin

これらは 可変長関数 と呼ばれます。ウィキペディアのリスト C++のコード例

Cプログラミング言語で可変機能関数を移植可能に実装するには、標準の stdarg.h ヘッダーファイルを使用する必要があります。古いvarargs.hヘッダーは、stdarg.hを支持して廃止されました。 C++では、ヘッダーファイルcstdargを使用する必要があります。

可変個の関数を作成するには、パラメーターリストの末尾に省略記号(_..._)を配置する必要があります。関数の本体内で、タイプ_va_list_の変数を定義する必要があります。その後、マクロva_start(va_list, last fixed param)va_arg(va_list, cast type)va_end(va_list)を使用できます。例えば:

_#include <stdarg.h>

double average(int count, ...)
{
    va_list ap;
    int j;
    double tot = 0;
    va_start(ap, count); //Requires the last fixed parameter (to get the address)
    for(j=0; j<count; j++)
        tot+=va_arg(ap, double); //Requires the type to cast to. Increments ap to the next argument.
    va_end(ap);
    return tot/count;
}
_
43
Stephan202

実際のC++ソリューションは可変長テンプレートです。かなり最近のコンパイラが必要で、必要に応じてC++ 11サポートを有効にします。

「すべての関数引数で同じことを行う」問題を処理する2つの方法:再帰的に、およびandい(しかし非常に非常に標準に準拠)ソリューション。

再帰解 は次のようになります。

template<typename... ArgTypes>
void print(ArgTypes... args);
template<typename T, typename... ArgTypes>
void print(T t, ArgTypes... args)
{
  std::cout << t;
  print(args...);
}
template<> void print() {} // end recursion

引数のコレクションごとに1つのシンボルを生成し、次に再帰へのステップごとに1つのシンボルを生成します。これは控えめに言っても準最適であるため、 SOの素晴らしいC++の人々 の考え方 リスト初期化の副作用を悪用する素晴らしいトリック

struct expand_type {
  template<typename... T>
  expand_type(T&&...) {}
};
template<typename... ArgTypes>
void print(ArgTypes... args)
{ 
  expand_type{ 0, (std::cout << args, 0)... };
}

コードは、わずかに異なる100万のテンプレートインスタンスに対して生成されません。また、ボーナスとして、関数引数の順序が保持されます。このソリューションの詳細については、他の回答を参照してください。

15
rubenvb

C++ 11以降では、初期化リストも使用できます。

int sum(const initializer_list<int> &il)
{
    int nSum = 0;
    for (auto x: il) 
        nSum += x;
    return nsum;
}

cout << sum( { 3, 4, 6, 9 } );
6
Seb

他の答えとは別に、整数の配列を渡そうとしているだけの場合は、次のようにします。

void func(const std::vector<int>& p)
{
    // ...
}

std::vector<int> params;
params.Push_back(1);
params.Push_back(2);
params.Push_back(3);

func(params);

ただし、パラメータ、フォームで呼び出すことはできません。回答に記載されている可変個の関数のいずれかを使用する必要があります。 C++ 0xでは、変数テンプレートを使用できます。これにより、タイプセーフになりますが、現時点では基本的にはメモリとキャストです。

ある種の可変パラメータ->ベクトルの事をエミュレートできます:

// would also want to allow specifying the allocator, for completeness
template <typename T> 
std::vector<T> gen_vec(void)
{
    std::vector<T> result(0);
    return result;
}

template <typename T> 
std::vector<T> gen_vec(T a1)
{
    std::vector<T> result(1);

    result.Push_back(a1);

    return result;
}

template <typename T> 
std::vector<T> gen_vec(T a1, T a2)
{
    std::vector<T> result(1);

    result.Push_back(a1);
    result.Push_back(a2);

    return result;
}

template <typename T> 
std::vector<T> gen_vec(T a1, T a2, T a3)
{
    std::vector<T> result(1);

    result.Push_back(a1);
    result.Push_back(a2);
    result.Push_back(a3);

    return result;
}

// and so on, boost stops at nine by default for their variadic templates

使用法:

func(gen_vec(1,2,3));
5
GManNickG

GManNickGとChristophの回答は優れていますが、可変個の関数を使用すると、整数だけでなく、任意の...パラメーターをプッシュできます。 willfutureにしたい場合、多くの変数とdifferent型の値を、変数関数を使用せずに関数にプッシュします。あなたにとって難しいか複雑すぎる、またはあなたはそれを使用する方法が好きではない、またはあなたはそれを使用するために必要なヘッダーを含めたくない場合は、常に使用することができますvoid**パラメーター。

たとえば、Stephan202の投稿:

double average(int count, ...)
{
    va_list ap;
    int j;
    double tot = 0;
    va_start(ap, count); //Requires the last fixed parameter (to get the address)
    for(j=0; j<count; j++)
        tot+=va_arg(ap, double); //Requires the type to cast to. Increments ap to the next argument.
    va_end(ap);
    return tot/count;
}

これは次のように書くこともできます。

double average(int count, void** params)
{
    int j;
    double tot = 0;
    for (j=0; j<count; j++)
       tot+=*(double*)params[j];
    return tot/count;
}

次のように使用します。

int _tmain(int argc, _TCHAR* argv[])
{
    void** params = new void*[3];
    double p1 = 1, p2 = 2, p3 = 3;
    params[0] = &p1;
    params[1] = &p2;
    params[2] = &p3;
    printf("Average is: %g\n", average(3, params));
    system("pause");
    return 0;
}

完全なコードの場合:

#include "stdafx"
#include <process.h>

double average(int count, void** params)
{
    int j;
    double tot = 0;
    for (j=0; j<count; j++)
        tot+=*(double*)params[j];
    return tot/count;
}

int _tmain(int argc, _TCHAR* argv[])
{
    void** params = new void*[3];
    double p1 = 1, p2 = 2, p3 = 3;
    params[0] = &p1;
    params[1] = &p2;
    params[2] = &p3;
    printf("Average is: %g\n", average(3, params));
    system("pause");
    return 0;
 }

出力:

平均:2

何かキーを押すと続行します 。 。 。

1
user2133061

私はc ++ビルダーxe.xxでこのようにします:

String s[] = {"hello ", " unli", " param", " test"};
String ret = BuildList(s, 4);

String BuildList(String s[], int count)
{
    for(int i = 0; i < count; i++)
    {
        //.... loop here up to last s[i] item ... 
    }
}
1
a_asiado

移植性を気にしない場合は、 gccのステートメント式 を使用して このC99コード をC++に移植できます。

#include <cstdio>

int _sum(size_t count, int values[])
{
    int s = 0;
    while(count--) s += values[count];
    return s;
}

#define sum(...) ({ \
    int _sum_args[] = { __VA_ARGS__ }; \
    _sum(sizeof _sum_args / sizeof *_sum_args, _sum_args); \
})

int main(void)
{
    std::printf("%i", sum(1, 2, 3));
}

C++ 0x 'ラムダ式でも同様のことができますが、私が使用しているgccバージョン(4.4.0)ではサポートされていません。

1
Christoph