Kelebihan fungsi lambda

14

Bagaimana cara membebani fungsi lambda lokal yang sederhana?

SSE masalah asli:

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

Pesan kesalahan

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

Tolong jangan keberatan tidak memeriksa input pengguna, ini adalah SSE.

mengintip
sumber
7
Lambdas bukan fungsi, mereka adalah objek sehingga kelebihan tidak pernah berlaku untuk mereka. translatehanya variabel lokal yang tidak dapat menggunakan kembali nama yang sama.
user7860670
2
related / dupe: stackoverflow.com/questions/32475576/…
NathanOliver

Jawaban:

10

Tidak, Anda tidak bisa membebani lambda!

Lambda adalah functor anonim (yaitu objek fungsi yang tidak disebutkan namanya), dan bukan fungsi sederhana. Karenanya, membebani benda-benda itu tidak mungkin dilakukan Apa yang pada dasarnya Anda coba lakukan hampir

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

Yang tidak mungkin, karena nama variabel yang sama tidak dapat digunakan kembali dalam C ++.


Namun, dalam kita memiliki if constexprsatu yang dapat instantiate satu-satunya cabang yang benar pada waktu kompilasi.

Berarti solusi yang mungkin adalah:

Menggunakan templat variabe Anda bisa melakukan sesuatu seperti. ( Lihat demo langsung online )

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

dan menyebutnya seperti

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

Menggunakan lambda generik (sejak ), yang di atas adalah: ( Lihat demo online secara langsung )

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

dan panggil lambda seperti yang Anda lakukan sekarang:

int r = translate(static_cast<int>(line[0]));
int c = translate(static_cast<char>(line[1]));
JeJo
sumber
3
Saya menemukan ini luar biasa
snoopy
1
Pertama, else ifkebutuhan Anda else if constexpr. Kedua, mengapa menggunakan templat variabel? Anda bisa menjadikan lambda generik dan checl Anda akan menjadi if constexpr (std::is_same_v<decltype(idx), int>)danelse if constexpr (std::is_same_v<decltype(idx), char>)
NathanOliver
6

Lambdas pada dasarnya adalah gula sintaksis untuk functors yang ditentukan secara lokal. Sejauh yang saya tahu, mereka tidak pernah dimaksudkan untuk kelebihan beban dipanggil dengan parameter yang berbeda. Perhatikan bahwa setiap ekspresi lambda adalah tipe yang berbeda, sehingga meskipun kesalahan langsung disingkirkan, kode Anda tidak dapat berfungsi sebagaimana dimaksud.

Namun Anda dapat, mendefinisikan functor dengan kelebihan beban operator(). Ini akan persis seperti yang Anda dapatkan dari lambdas jika memungkinkan. Anda hanya tidak mendapatkan sintaks singkat.

Sesuatu seperti:

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;
}
idclev 463035818
sumber
tunggu sebentar, Anda memanggil sintaks lambda bagus ??
user7860670
1
@ VTT itu bagus bahwa sintaksnya singkat. Dibandingkan dengan beberapa hal kuno yang tidak terlalu buruk
idclev 463035818
5

Jadi aturan untuk overloading nama hanya berlaku untuk jenis pencarian nama fungsi tertentu (baik gratis maupun metode).

Lambdas bukan fungsi, mereka objek dengan operator fungsi-panggilan. Jadi kelebihan muatan tidak dapat terjadi antara dua lambda yang berbeda.

Sekarang, Anda bisa mendapatkan resolusi kelebihan untuk bekerja dengan objek fungsi, tetapi hanya dalam lingkup objek tunggal. Dan kemudian jika ada lebih dari satu operator(), resolusi kelebihan dapat memilih di antara mereka.

Namun, lambda tidak memiliki cara yang jelas untuk memiliki lebih dari satu operator(). Kita dapat menulis kelas utilitas sederhana (dalam ) untuk membantu kami:

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

dan panduan deduksi:

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

dengan dua ini, kita bisa membebani dua lambda:

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]));

dan dilakukan.

Menulis overloadeddimungkinkan di dan tetapi membutuhkan lebih banyak pekerjaan dan kurang elegan. Setelah Anda mengetahui masalahnya, menemukan solusi yang cocok dengan apa yang didukung kompiler khusus Anda di jalan fitur C ++ seharusnya tidak sulit.

Yakk - Adam Nevraumont
sumber
Seperti yang saya pahami, setiap lamda yang "kelebihan beban" memiliki blok tangkapnya sendiri, yaitu lambda yang tidak berbagi apa pun (dan mungkin menghabiskan waktu CPU untuk mengambil data yang sama berulang-ulang). Adakah kemungkinan standar C ++ akan memiliki sesuatu untuk memperbaikinya? Atau hanya opsi variadic generic lamda+ if constexpruntuk memisahkan panggilan?
CM
@ CM Untuk mengajukan pertanyaan tentang stack overflow, silakan tekan tombol [Ajukan Pertanyaan] di kanan atas Anda, bukan tombol [Tambah Komentar]. Terima kasih!
Yakk - Adam Nevraumont