람다의 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 ...
};

따라서 두 가지 다른 유형이 컴파일러에서 생성되어 유형이 호환되지 않습니다. 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 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 람다 자체에는 동일한 서명이 있으므로 컴파일러는 구현 세부 정보를 숨기고 더 이해하기 쉬운 오류를 제공하기 위해 동일한 람다의 위치와 서명을 제공합니다. 그러나 결국 그들은 여전히 ​​다음 SomeCompilerGeneratedTypeName1과 같이 해석됩니다.SomeCompilerGeneratedTypeName2
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
그러나 Visual Studio에서는 작동하지 않습니다. 람다가 다른 부름으로 변신 할 수있게 해주는 확장은 그것을 막는다.
기 illa 라치 콧

@GuillaumeRacicot,이 쪽지 감사합니다. 그것에 대해 더 많이 읽을 수있는 링크를 제공해 주시겠습니까?
Evg.


2
@GuillaumeRacicot 최근 MSVC 버전에서 컴파일되는 것 같습니다. godbolt.org/z/ZQLWxy
브라이언

@ 브라이언 오! 이것은 훌륭한 소식입니다. 이제 코드를 변경해야합니다. 감사!
기 illa 라치 콧

10

컴파일러는 어떤 유형을 사용 auto해야하는지 결정할 수 없습니다 .

auto lambda = condition ? lambda1 : lambda2;

모든 람다는 다르고 독특한 유형이기 때문에.

작동하는 한 가지 방법은 다음과 같습니다.

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 람다 ( lambda1lambda2)는 서로 다른 유형이므로 from 및에 ?:대한 반환 유형을 추론 할 수 없습니다 . 이 2는 서로 변환 할 수 없기 때문에 발생합니다.lambdalambda1lambda2

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.