ラムダのC ++三項割り当て


11

次のスニペットがコンパイルされない理由は何ですか?「エラー:オペランドの?:タイプが異なります」というエラーが表示されます。

  auto lambda1 = [&](T& arg) {
      ...
  };
  auto lambda2 = [&](T& arg) {
      ...
  };
  auto lambda = condition ? lambda1 : lambda2;

回答:


11

個々のラムダは、コンパイラーによって異なるクラスに変換されます。たとえば、lambda1の定義は次と同等です。

class SomeCompilerGeneratedTypeName {
public:
  SomeCompilerGeneratedTypeName(...) { // Capture all the required variables here
  }

  void operator()(T& arg) const {
    // ...
  }

private:
  // All the captured variables here ...
};

したがって、2つの異なるタイプがコンパイラーによって生成され、これにより、タイプの非互換性が発生します。 auto lambda = condition ? lambda1 : lambda2;

以下はうまくいくでしょう:

auto lambda = condition ? std::function<void(T&)>(lambda1) : std::function<void(T&)>(lambda2);

両方のラムダが実際に異なる型であることを強調するために<typeinfo>、標準ライブラリとtypeid演算子から使用できます。ラムダはポリモーフィック型ではないため、標準では「typeid」演算子がコンパイル時に評価されることが保証されています。これは、RTTIが無効になっていても、次の例が有効であることを示しています。

#include <iostream>
#include <typeinfo>

int main()
{
    struct T {

    };

    auto lambda1 = [&](T& arg) {
        return;
    };

    auto lambda2 = [&](T& arg) {
      return;
    };

    std::cout << typeid(lambda1).name() << "/" << typeid(lambda1).hash_code() << std::endl;
    std::cout << typeid(lambda2).name() << "/" << typeid(lambda2).hash_code() << std::endl;

    return 0;
}

プログラムの出力は次のとおりです(GCC 8.3を使用、Goboltを参照):

Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066

完全なエラーは、「エラー:オペランド?:異なるタイプ 'f(const std :: vector <int>&、size_t、size_t)[with T = unsigned char; size_t = long unsigned int] :: <lambda(unsigned char& )> 'および' f(const std :: vector <int>&、size_t、size_t)[with T = unsigned char; size_t = long unsigned int] :: <lambda(unsigned char&)> '"、ここにすべてのタイプとフォーマットが同じです。

1
@cowラムダ自体には同じ署名があるため、コンパイラーは、その実装の詳細を非表示にし、より理解しやすいエラーを提供するために、同一の両方のラムダの場所と署名を提供します。しかし、最終的に、彼らはまだあると解釈されているSomeCompilerGeneratedTypeName1SomeCompilerGeneratedTypeName2
Xatyrian

1
@cow私は答えの始まりを強調する例を追加しました、あなたはそれが面白いと感じるかもしれません
Xatyrian

12

奇妙なことに、ラムダがキャプチャレスである場合、オペレーター+トリックを使用できます。

auto lambda1 = [](int arg) { ... };
auto lambda2 = [](int arg) { ... };

auto lambda = condition ? +lambda1 : +lambda2; // This compiles!
lambda(2019); 

これ+は機能します。これは、がラムダを関数ポインタに変換し、両方の関数ポインタが同じ型(のようなものvoid (*)(int))を持っているためです。

GCCとClang(MSVC +では不可)を使用すると、省略できますが、ラムダは関数ポインターに変換されます。


1
ただし、これはビジュアルスタジオでは機能しません。ラムダが異なる呼び出し変換に変換できるようにする拡張機能は、それを防ぎます。
ギヨームラシコット

@GuillaumeRacicot、このメモをありがとう。詳細を確認できるリンクをお願いします。
平均


2
@GuillaumeRacicot最近のMSVCバージョンでコンパイルできるようです。godbolt.org/z/ZQLWxy
ブライアン、

@ブライアン・オー!これは素晴らしいニュースです。今、私はいくつかのコードを変更する必要があります。ありがとう!
ギヨームラシコット

10

コンパイラーは、タイプautoを決定できません。

auto lambda = condition ? lambda1 : lambda2;

すべてのラムダには異なるユニークなタイプがあるからです。

機能する方法の1つは次のとおりです。

auto lambda = [&](T& arg) {
     return (condition ? lambda1(arg) : lambda2(arg));
}

8

各ラムダには一意の型があるため、コンパイルされません?:。の共通の型はありません。

あなたはそれらをstd::function<void(T&)>例えばで包むことができます

auto lamba1 = [&](T& arg) {
  ...
};
auto lambda2 = [&](T& arg) {
  ...
};
auto lambda = condition ? std::function(lambda1) : lambda2; // C++17 class template deduction

8

2つのラムダ(lambda1およびlambda2)は2つの異なる?:型であるためlambda、from lambda1およびの戻り型を推定できませんlambda2。これは、これら2つが相互に変換できないために発生します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.