5

以下のコードは、foo の呼び出しがあいまいであるため、gcc 4.5 ではコンパイルされません。それを明確にする正しい方法は何ですか?

#include <iostream>
#include <functional>
using namespace std;

void foo(std::function<void(int, int)> t)
{
    t(1, 2);
}

void foo(std::function<void(int)> t)
{
    t(2);
}

int main()
{
    foo([](int a, int b){ cout << "a: " << a << " b: " << b << endl;});
}
4

2 に答える 2

6

最良の方法はstd::function、正しいタイプのオブジェクトを明示的に作成してから、そのオブジェクトを関数に渡すことです。

std::function<void(int, int)> func = 
    [](int a, int b) { cout << "a: " << a << " b: " << b << endl; }
foo(func);

またはインライン:

foo(
    std::function<void(int, int)>(
        [](int a, int b) { cout << "a: " << a << "b: " << b << endl; }
));

std::function何でも受け入れるコンストラクターテンプレートがあります。

template<class F> function(F);

このため、オーバーロードの解決中にコンパイラがfooどちらを選択するかを知る方法はありません。両方とも、ラムダ式を引数として取ることができるコンストラクタを持っていますstd::function<void(int)>std::function<void(int, int)>

std::functionオブジェクトを直接渡す場合std::function、オーバーロードの解決時にはコピーコンストラクターが優先されるため、コンストラクターテンプレートの代わりにコピーコンストラクターが選択されます。


将来への回答: キャプチャリストが空であることが保証されている場合は、通常の関数ポインタを使用することもできます。C ++ 0xでは、キャプチャレスラムダは暗黙的に関数ポインタに変換可能です。だから、あなたは次のようなものを使うことができます

void foo(void (*t)(int, int)) { t(1, 2); }

void foo(void (*t)(int)) { t(1); }

fooキャプチャレスラムダ(または一致する型の関数ポインタ)を使用して直接呼び出します。

この変換は、ドラフト言語標準(今年の2月に追加された)へのごく最近の追加であるため、まだ広くサポートされていない可能性があることに注意してください。Visual C++2010はまだサポートしていません。最新のg++についてはわかりません。

于 2010-11-06T02:56:10.593 に答える
3

私は最近、同様の問題について考えていましたが、既知の解決策を探しているときに、この投稿に出くわし、解決するための解決策がありません

別の解決策は、ファンクターをテンプレート引数として抽象化し、decltype を使用してその型を解決することです。したがって、上記の例は次のようになります。

#include <iostream>
#include <functional>
using namespace std;

template<class F>
auto foo(F t) -> decltype(t(1,2))
{
    t(1, 2);
}

template<class F>
auto foo(F t) -> decltype(t(2)) 
{
    t(2);
}

int main()
{
     foo([](int a, int b){ cout << "a: " << a << " b: " << b << endl;});
}

これは、gcc 4.5 で期待どおりに機能します。

于 2011-07-29T04:20:31.177 に答える