106

次のコードでは、 への最初の呼び出しfooがあいまいなため、コンパイルに失敗します。

ラムダの前に追加された 2 番目は+、関数ポインターのオーバーロードに解決されます。

#include <functional>

void foo(std::function<void()> f) { f(); }
void foo(void (*f)()) { f(); }

int main ()
{
    foo(  [](){} ); // ambiguous
    foo( +[](){} ); // not ambiguous (calls the function pointer overload)
}

+ここで表記は何をしているのですか?

4

1 に答える 1

110

+式の+[](){}は単項演算+子です。[expr.unary.op]/7 で次のように定義されています。

単項演算子のオペランドは、+算術、スコープなし列挙、またはポインター型を持ち、結果は引数の値になります。

ラムダは算術型などではありませんが、変換できます。

[expr.prim.lambda]/3

ラムダ式[...]の型は、名前のない一意の非共用体クラス型 (クロージャー型と呼ばれます) であり、そのプロパティについては以下で説明します。

[expr.prim.lambda]/6

lambda-captureを持たないlambda -expressionのクロージャー型には、クロージャー型の関数呼び出し演算子と同じパラメーターと戻り値の型を持つ関数へのポインターへの非変換関数があります。この変換関数によって返される値は、呼び出されたときにクロージャー型の関数呼び出し演算子を呼び出すのと同じ効果を持つ関数のアドレスでなければなりません。publicvirtualexplicit const

したがって、単項+は、この lambda 用の関数ポインター型への変換を強制しますvoid (*)()。したがって、式の型+[](){}はこの関数ポインタ型void (*)()です。

2 番目のオーバーロードvoid foo(void (*f)())は、オーバーロード解決のランキングで完全一致になるため、明確に選択されます (最初のオーバーロードは完全一致ではないため)。


ラムダは、およびの要件を満たす任意の型を取るの非明示的なテンプレート ctor[](){}を介して に変換できます。std::function<void()>std::functionCallableCopyConstructible

ラムダは、クロージャー型void (*)()の変換関数を介して変換することもできます(上記を参照)。

どちらもユーザー定義の変換シーケンスであり、ランクは同じです。そのため、あいまいさのために最初の例でオーバーロードの解決が失敗します。


Daniel Krügler の議論に裏付けられた Cassio Neri によると、この単項+トリックは動作を指定する必要があります。つまり、それに依存できます (コメントの議論を参照)。

それでも、あいまいさを避けたい場合は、関数ポインター型への明示的なキャストを使用することをお勧めします: SO で何が機能し、なぜ機能するのかを尋ねる必要はありません;)

于 2013-07-23T22:41:02.063 に答える