7

次のコードを検討してください。

main()
{
    bool t;
    ...
    std::function<bool (bool)> f = t ? [](bool b) { return b; } : [](bool b) { return !b; }; // OK
    std::function<bool (bool)> f = t ? [t](bool b) { return t == b; } : [t](bool b) { return t != b; }; // error
}

Clang 3.1 でコンパイルすると、非キャプチャ ラムダの割り当ては機能しますが、キャプチャのあるラムダは失敗します。

main.cpp:12:36: error: incompatible operand types ('<lambda at main.cpp:12:38>' and '<lambda at main.cpp:12:71>')
        std::function<bool (bool)> f2 = t ? [t](bool b) { return t == b; } : [t](bool b) { return t != b; }; // error
                                          ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

同じ変数をキャプチャすると、2 つのラムダが互換性のない型になるのはなぜですか?

4

1 に答える 1

16

ラムダの型は、クロージャ型と呼ばれる「一意の非ユニオンクラス型」です。各ラムダは、宣言のスコープに対してローカルな異なる型として実装され、関数本体を呼び出すためのオーバーロードされた演算子()があります。

:これを書く場合:

auto a=[t](bool b){return t==b;};
auto b=[t](bool b){return t!=b;};

次に、コンパイラはこれを(多かれ少なかれ)コンパイルします。

class unique_lambda_name_1 
{
 bool t; 
public:
 unique_lambda_name_1(bool t_) t(_t) {}
 bool operator () (bool b) const { return t==b; }
} a(t); 
class unique_lambda_name_2
{
 bool t;
public: 
 unique_lambda_name_2(bool t_) t(_t) {}
 bool operator () (bool b) const { return t!=b; }
} b(t); 

aとbはタイプが異なり、?:演算子では使用できません。

ただし、§5.1.2(6)によると、キャプチャのないラムダのクロージャタイプには、ラムダを関数ポインタに変換する非明示的なパブリック変換演算子があります。非クロージャは単純な関数として実装できます。同じ引数と戻り型を持つラムダはすべて同じ型のポインターに変換できるため、三項?:演算子を適用できます。

例:非キャプチャラムダ:

auto c=[](bool b){return b;};

このように実装されます:

class unique_lambda_name_3
{
 static bool body(bool b) { return b; }
 public:
 bool operator () (bool b) const { return body(b); }
 operator decltype(&body) () const { return &body; }
} c; 

つまり、この行は次のようになります。

auto x = t?[](bool b){return b;}:[](bool b){return !b;};

実際にはこれを意味します:

// a typedef to make this more readable
typedef bool (*pfun_t)(bool); 
pfun_t x = t?((pfun_t)[](bool b){return b;}):(pfun_t)([](bool b){return !b;});
于 2012-07-10T05:07:20.400 に答える