Chuyển nhượng ternary C ++ của lambda


11

Bất cứ ý tưởng tại sao đoạn trích sau không biên dịch? Nó phàn nàn với lỗi "error: operands to ?: Có các loại khác nhau"

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

Câu trả lời:


11

Trình biên dịch riêng lẻ được dịch sang các lớp khác nhau bởi trình biên dịch. Ví dụ: định nghĩa của lambda1 tương đương với:

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

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

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

Do đó, hai loại khác nhau được tạo bởi trình biên dịch, điều này gây ra sự không tương thích về kiểu cho auto lambda = condition ? lambda1 : lambda2;

Sau đây sẽ làm việc:

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

Để làm nổi bật rằng cả hai lambdas thực sự là các loại khác nhau, chúng ta có thể sử dụng <typeinfo>từ thư viện tiêu chuẩn và typeidtoán tử. Lambdas không phải là loại đa hình, do đó, tiêu chuẩn đảm bảo rằng toán tử 'typeid' được đánh giá tại thời điểm biên dịch. Điều này cho thấy ví dụ sau là hợp lệ ngay cả khi RTTI bị tắt:

#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;
}

Đầu ra của chương trình là (với GCC 8.3, xem trên Gobolt ):

Z4mainEUlRZ4mainE1TE_/7654536205164302515
Z4mainEUlRZ4mainE1TE0_/10614161759544824066

Lỗi hoàn toàn là "error: operands to ?: Có các loại khác nhau 'f (const std :: vector <int> &, size_t, size_t) [với T = unsign char; size_t = long unsign int] :: <lambda (unsign char & )> 'và' f (const std :: vector <int> &, size_t, size_t) [với T = unsign char; size_t = long unsign int] :: <lambda (uns uns char &)> '", trong đó tôi thấy giống hệt tất cả các loại và định dạng.

1
@cow Vì bản thân lambda có cùng chữ ký nên trình biên dịch, để ẩn chi tiết triển khai của nó và đưa ra một lỗi dễ hiểu hơn, cung cấp cho bạn vị trí và chữ ký của cả hai lambdas giống hệt nhau. Nhưng cuối cùng, chúng vẫn được hiểu là SomeCompilerGeneratedTypeName1SomeCompilerGeneratedTypeName2
Xatyrian

1
@cow tôi đã thêm một ví dụ làm nổi bật phần đầu của câu trả lời, bạn có thể thấy nó thú vị
Xatyrian 11/11/19

12

Thật kỳ lạ, nếu lambdas không bị bắt, +có thể sử dụng thủ thuật toán tử :

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

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

Điều này hoạt động, bởi vì +sẽ chuyển lambda thành một con trỏ hàm và cả hai con trỏ hàm có cùng loại (giống như void (*)(int)).

Với GCC và Clang (nhưng không phải với MSVC), +có thể được bỏ qua, lambdas vẫn sẽ được chuyển đổi thành con trỏ hàm.


1
Điều này sẽ không làm việc trên studio hình ảnh mặc dù. Phần mở rộng của chúng cho phép lambda chuyển đổi sang các đường dẫn gọi khác nhau ngăn chặn nó.
Trường đua Guillaume

@GuillaumeRacicot, cảm ơn vì lưu ý này. Bạn có thể vui lòng cho một liên kết nơi tôi có thể đọc thêm về nó?
Evg


2
@GuillaumeRacicot Dường như nó được biên dịch trên phiên bản MSVC gần đây. godbolt.org/z/ZQLWxy
Brian

@Brian ơi! Đây là tin tuyệt vời. Bây giờ tôi phải thay đổi một số mã. Cảm ơn!
Guillaume Racicot

10

Trình biên dịch không thể quyết định loại nào autosẽ là:

auto lambda = condition ? lambda1 : lambda2;

vì mỗi lambda có một loại khác nhau và độc đáo.

Một cách sẽ làm việc là:

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

8

Nó không biên dịch vì mỗi lambda có một loại duy nhất, không có loại chung cho ?:.

Bạn có thể gói chúng vào std::function<void(T&)>, vd

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

8

Vì 2 lambdas ( lambda1lambda2) là 2 loại khác nhau, ?:không thể suy ra loại trả về cho lambdatừ lambda1lambda2. Điều này xảy ra bởi vì 2 cái này không thể chuyển đổi lẫn nhau.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.