Quá tải một chức năng lambda


14

Làm thế nào để quá tải một chức năng lambda địa phương đơn giản?

SSE của vấn đề ban đầu:

#include <iostream>
#include <map>

void read()
{
    static std::string line;
    std::getline(std::cin, line);

    auto translate = [](int idx)
    {
        constexpr static int table[8]{ 7,6,5,4,3,2,1,0 };
        return table[idx];
    };

    auto translate = [](char c)
    {
        std::map<char, int> table{ {'a', 0}, {'b', 1}, {'c', 2}, {'d', 3},
                                             {'e', 4}, {'f', 5}, {'g', 6}, {'h', 7} };
        return table[c];
    };

    int r = translate(static_cast<int>(line[0]));
    int c = translate(static_cast<char>(line[1]));
    std::cout << r << c << std::endl;
}

int main()
{
    read();
    return 0;
}

Các thông báo lỗi

error: conflicting declaration 'auto translate'
note: previous declaration as 'read()::<lambda(int)> translate'

Xin lưu ý không kiểm tra đầu vào của người dùng, đây là SSE.


7
Lambdas không phải là chức năng, chúng là đối tượng nên quá tải không bao giờ áp dụng cho chúng. translatechỉ là các biến cục bộ không thể sử dụng lại cùng tên.
dùng7860670

Câu trả lời:


10

Không, bạn không thể quá tải lambda!

Lambdas là functor nặc danh (tức là các đối tượng hàm không tên) và không phải là các hàm đơn giản. Do đó, quá tải những đối tượng không thể. Những gì bạn về cơ bản cố gắng làm là gần như

struct <some_name>
{
    int operator()(int idx) const
    {
        return {}; // some int
    }
}translate; // >>> variable name

struct <some_name>
{
    int operator()(char idx) const
    {
        return {}; // some int
    }
}translate; // >>> variable name

Điều này là không thể, vì cùng một tên biến không thể được sử dụng lại trong C ++.


Tuy nhiên, trong chúng ta có if constexprthể khởi tạo nhánh duy nhất đúng với thời gian biên dịch.

Ý nghĩa của các giải pháp có thể là:

  • Một mẫu lambda duy nhất . hoặc là
  • Một lambda chung và tìm loại tham số sử dụng decltype cho if constexprkiểm tra. (Tín dụng @NathanOliver )

Sử dụng mẫu matrixbe bạn có thể làm một cái gì đó như. ( Xem bản demo trực tuyến )

#include <type_traits> // std::is_same_v

template<typename T>
constexpr auto translate = [](T idx) 
{
    if constexpr (std::is_same_v<T, int>)
    {
        constexpr static int table[8]{ 7,6,5,4,3,2,1,0 };
        return table[idx];
    }
    else if constexpr (std::is_same_v<T, char>)
    {
        std::map<char, int> table{ {'a', 0}, {'b', 1}, {'c', 2}, {'d', 3}, {'e', 4}, {'f', 5}, {'g', 6}, {'h', 7} };
        return table[idx];
    }
};

và gọi nó như

int r = translate<int>(line[0]);
int c = translate<char>(line[1]);

Sử dụng lambda chung (kể từ ), ở trên sẽ là: ( Xem bản demo trực tuyến )

#include <type_traits> // std::is_same_v

constexpr auto translate = [](auto idx) 
{
    if constexpr (std::is_same_v<decltype(idx), int>)
    {
        constexpr static int table[8]{ 7,6,5,4,3,2,1,0 };
        return table[idx];
    }
    else if constexpr (std::is_same_v<decltype(idx), char>)
    {
        std::map<char, int> table{ {'a', 0}, {'b', 1}, {'c', 2}, {'d', 3}, {'e', 4}, {'f', 5}, {'g', 6}, {'h', 7} };
        return table[idx];
    }
};

và gọi lambda như bạn làm bây giờ:

int r = translate(static_cast<int>(line[0]));
int c = translate(static_cast<char>(line[1]));

3
Tôi thấy điều này thật tuyệt vời
snoopy

1
Đầu tiên, bạn else ifcần phải có else if constexpr. Thứ hai, tại sao sử dụng một mẫu biến? Bạn chỉ có thể làm cho lambda chung chung và checls của bạn sẽ trở thành if constexpr (std::is_same_v<decltype(idx), int>)else if constexpr (std::is_same_v<decltype(idx), char>)
NathanOliver

6

Lambdas về cơ bản là đường cú pháp cho functor được xác định tại địa phương. Theo tôi biết, chúng không bao giờ có nghĩa là quá tải để được gọi với các tham số khác nhau. Lưu ý rằng mỗi biểu thức lambda thuộc một loại khác nhau, do đó, ngay cả lỗi ngay lập tức, mã của bạn không thể hoạt động như dự định.

Tuy nhiên, bạn có thể xác định một functor bị quá tải operator(). Đây sẽ chính xác là những gì bạn nhận được từ lambdas nếu có thể. Bạn chỉ không nhận được cú pháp ngắn gọn.

Cái gì đó như:

void read()
{
    static std::string line;

    struct translator {
          int operator()(int idx) { /* ... */ }
          int operator()(char x)  { /* ... */ }
    };
    translator translate;


    std::getline(std::cin, line);

    int r = translate(static_cast<int>(line[0]));
    int c = translate(static_cast<char>(line[1]));

    std::cout << r << c << std::endl;
}

chờ một chút, bạn đang gọi cú pháp lambda tốt chứ ??
dùng7860670

1
@VTT thật tuyệt khi cú pháp ngắn gọn. So với một số thứ cổ xưa hơn, nó không quá tệ
idclev 463035818

5

Vì vậy, các quy tắc cho quá tải tên chỉ áp dụng cho một số loại tra cứu tên hàm (cả miễn phí và phương thức).

Lambdas không phải là hàm, chúng là các đối tượng với toán tử gọi hàm. Vì vậy, quá tải không thể xảy ra giữa hai lambdas khác nhau.

Bây giờ, bạn có thể có được độ phân giải quá tải để làm việc với các đối tượng hàm, nhưng chỉ trong phạm vi của một đối tượng. Và sau đó nếu có nhiều hơn một operator(), độ phân giải quá tải có thể chọn giữa chúng.

Một lambda, tuy nhiên, không có cách rõ ràng để có nhiều hơn một operator(). Chúng tôi có thể viết một lớp tiện ích đơn giản (trong ) để giúp chúng tôi:

template<class...Fs>
struct overloaded : Fs... {
  using Fs::operator()...;
};

và hướng dẫn khấu trừ:

template<class...Fs>
overloaded(Fs...) -> overloaded<Fs...>;

với hai cái này, chúng ta có thể quá tải hai lambdas:

static std::string line;
std::getline(std::cin, line);

auto translate_int = [](int idx){
    constexpr static int table[8] {7,6,5,4,3,2,1,0};
    return table[idx];
};

auto translate_char = [](char c) {
    std::map<char, int> table { {'a', 0}, {'b', 1}, {'c', 2}, {'d', 3},
                                {'e', 4}, {'f', 5}, {'g', 6}, {'h', 7} };
    return table[c];
};
auto translate = overloaded{ translate_int, translate_char };

int r = translate(static_cast<int>(line[0]));
int c = translate(static_cast<char>(line[1]));

và thực hiện.

Viết overloadedcó thể trong cả nhưng đòi hỏi nhiều công việc hơn và kém thanh lịch. Khi bạn nhận thức được vấn đề, việc tìm một giải pháp phù hợp với những gì trình biên dịch cụ thể của bạn hỗ trợ theo cách của các tính năng C ++ không nên khó khăn.


Theo tôi hiểu, mỗi lamda "quá tải" đều có khối bắt riêng, tức là những lambdas này không chia sẻ bất cứ điều gì (và có thể lãng phí thời gian của CPU để ghi lại cùng một dữ liệu). Bất kỳ cơ hội nào tiêu chuẩn C ++ sẽ có cái gì đó để khắc phục điều đó? Hoặc chỉ có tùy chọn là variadic generic lamda+ if constexprđể tách các cuộc gọi?
CM

@CM Để đặt câu hỏi về lỗi tràn ngăn xếp, vui lòng nhấn nút [Đặt câu hỏi] ở trên cùng bên phải của bạn, không phải nút [Thêm nhận xét]. Cảm ơn!
Yakk - Adam Nevraumont
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.