9

ANSI C++ for_each ステートメントを使用して、標準ベクトルの要素を反復処理して出力しようとしています。for_each でオーバーロードされていない関数を呼び出すと機能しますが、オーバーロードされた関数を呼び出すとコンパイラ エラーが発生します。

コンパイラ エラーが発生する場所を示す最小限のテスト プログラムを次に示します。

#include <algorithm>
#include <iostream>
#include <vector>

struct S {
    char c;
    int i;
};
std::vector<S> v;

void print_struct(int idx);
void print_struct(const struct S& s);

// f: a non-overloaded version of the preceding function.
void f(const struct S& s);

int main()
{
    v.push_back((struct S){'a', 1});
    v.push_back((struct S){'b', 2});
    v.push_back((struct S){'c', 3});

    for (unsigned int i = 0; i < v.size(); ++i)
        print_struct(i);

    /* ERROR! */
    std::for_each(v.begin(), v.end(), print_struct);

    /* WORKAROUND: */
    std::for_each(v.begin(), v.end(), f);

    return 0;
}

// print_struct: Print a struct by its index in vector v.
void print_struct(int idx)
{
    std::cout << v[idx].c << ',' << v[idx].i << '\n';
}

// print_struct: Print a struct by reference.
void print_struct(const struct S& s)
{
    std::cout << s.c << ',' << s.i << '\n';
}

// f: a non-overloaded version of the preceding function.
void f(const struct S& s)
{
    std::cout << s.c << ',' << s.i << '\n';
}

以下を使用して、openSUSE 12.2 でこれをコンパイルしました。

g++-4.7 -ansi -Wall for_each.cpp -o for_each

完全なエラー メッセージは次のとおりです。

for_each.cpp: In function ‘int main()’:
for_each.cpp:31:48: error: no matching function for call to ‘for_each(std::vector<S>::iterator, std::vector<S>::iterator, <unresolved overloaded function type>)’
for_each.cpp:31:48: note: candidate is:
In file included from /usr/include/c++/4.7/algorithm:63:0,
                 from for_each.cpp:5:
/usr/include/c++/4.7/bits/stl_algo.h:4436:5: note: template<class _IIter, class _Funct> _Funct std::for_each(_IIter, _IIter, _Funct)
/usr/include/c++/4.7/bits/stl_algo.h:4436:5: note:   template argument deduction/substitution failed:
for_each.cpp:31:48: note:   couldn't deduce template parameter ‘_Funct’

この特定のエラーの検索結果は、スタック オーバーフローや一般的な Web では表示されません。どんな助けでも大歓迎です。

4

2 に答える 2

7

名前はオーバーロード セットを参照します。必要なオーバーロードを指定する必要があります。

std::for_each(v.begin(), v.end(), (void (&)(S const&)) print_struct);

もう 1 つのアプローチは、多態的な呼び出し可能な関数オブジェクトをヘルパーとして使用することです。

struct PrintStruct
{
    template <typename T> void operator()(T const& v) const 
        { return print_struct(v); }
};

int main()
{
    PrintStruct helper;

    std::vector<S> sv;
    std::vector<int> iv;

    // helper works for both:
    std::for_each(sv.begin(), sv.end(), helper);
    std::for_each(iv.begin(), iv.end(), helper);
于 2012-09-28T10:29:15.210 に答える
5

std::for_each宣言は次のようになります。

template<class InputIter, class Func>
void for_each(InputIter first, InputIter last, Func func);

ご覧のとおり、3 番目のパラメーターとして指定したものは何でも受け取ります。特定の署名の呼び出し可能な型または呼び出し可能な型である必要があるという制限はまったくありません。

オーバーロードされた関数を扱う場合、適切な関数を選択するためのコンテキストを提供しない限り、それらは本質的にあいまいです。オーバーロードされた関数の呼び出しでは、このコンテキストが引数になります。ただし、ポインターが必要な場合は、引数をコンテキストとして使用することはできません。また、for_eachパラメーターも何でも受け取るため、コンテキストとしてカウントされません。

関数パラメーターが適切なオーバーロードを選択するための有効なコンテキストになる可能性がある場所の例として、これを参照してください。

// our overloads
void f(int){}
void f(double){}

typedef void (*funcptr_type)(int);
void g(funcptr_type){}

// ...
g(&f); // will select 'void f(int)' overload, since that's 
       // the only valid one given 'g's parameter

ご覧のとおり、ここで明確なコンテキストを提供することで、コンパイラが適切なオーバーロードを選択し、あいまいにならないようにしています。std::for_eachのパラメーターは、何でも取るため、そのようなコンテキストを提供しません。

次の 2 つの解決策があります。

  • コンテキストを手動で提供する
    • 正しい関数ポインタ型へのキャスト、または
    • 正しい型の中間変数を使用してそれを渡す
  • オーバーロードされた関数にディスパッチする、オーバーロードされていない関数を使用します ( で行ったようにf)

C++11 では、2 番目のオプションにラムダを使用することもできます。

std::for_each(v.begin(), v.end(), [](const S& s){ print_struct(s); });

コードに関する注意事項:

  • (struct S){'a', 1}複合リテラルであり、標準 C++ ではありません
  • struct SC ++では必要ありません。S十分です
于 2012-09-28T10:36:18.497 に答える