Lambda fonksiyonunu aşırı yükle


14

Basit bir yerel lambda işlevi nasıl aşırı yüklenir?

Orijinal sorunun SSE'si:

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

Hata mesajları

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

Lütfen kullanıcı girişini kontrol etmeyin, bu bir SSE'dir.


7
Lambdalar işlev değildir, nesnelerdir, bu nedenle aşırı yükleme asla onlar için geçerli değildir. translateyalnızca aynı adı yeniden kullanamayan yerel değişkenlerdir.
user7860670

Yanıtlar:


10

Hayır, lambda'yı aşırı yükleyemezsin!

Lambdalar basit işlevler değil , anonim işlevlerdir (yani adsız işlev nesneleri). Bu nedenle, bu nesneleri aşırı yüklemek mümkün değildir. Temel olarak yapmaya çalıştığınız şey neredeyse

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

Bu mümkün değildir, çünkü aynı değişken adı C ++ ile yeniden kullanılamaz.


Ancak, içinde sahip olduğumuz if constexprhangi bir derleme zamanında doğrudur tek şube örneğini.

Yani olası çözümler:

  • Tek bir variabe şablonu lambda. veya
  • Genel bir lambda ve çek decltype için kullanılan parametrenin türünü bulun if constexpr. ( Credits @NathanOliver )

Variabe şablonu kullanarak bir şey yapabilirsiniz. ( Çevrimiçi canlı bir demoya bakı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];
    }
};

ve şöyle deyin

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

Jenerik lambda kullanarak ( beri ), yukarıdakiler şöyle olacaktır: ( Çevrimiçi canlı bir demoya bakı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];
    }
};

ve lambda'yı şimdi yaptığınız gibi arayın:

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

3
Bu şaşırtıcı buluyorum
snoopy

1
İlk olarak, else ifihtiyaçlarınız else if constexpr. İkincisi, neden değişken bir şablon kullanalım? Sadece lambda jenerik yapabilirsiniz ve checls olacak if constexpr (std::is_same_v<decltype(idx), int>)veelse if constexpr (std::is_same_v<decltype(idx), char>)
NathanOliver

6

Lambdalar temel olarak lokal olarak tanımlanmış functorlar için sözdizimsel şekerdir. Bildiğim kadarıyla, hiçbir zaman farklı parametrelerle çağrılmak için aşırı yüklenmeleri amaçlanmamıştı. Her lambda ifadesinin farklı bir türde olduğunu unutmayın, bu nedenle hemen hata bir kenara bile, kodunuzun istendiği gibi çalışamayacağını unutmayın.

Ancak, aşırı yüklenmiş bir functor tanımlayabilirsiniz operator(). Mümkünse lambdalardan alacağınız şey tam olarak bu olacak. Kısa sözdizimini alamazsınız.

Gibi bir şey:

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

Bir dakika, lambda sözdizimi güzel diyorsun ??
user7860670

1
@VTT sözdiziminin kısa olması güzel. Bazı eski şeylerle karşılaştırıldığında çok kötü değil
idclev 463035818

5

Bu nedenle, adları aşırı yüklemeye ilişkin kurallar, yalnızca işlev adlarının belirli türlerine (hem ücretsiz hem de yöntemler) bakmak için geçerlidir .

Lambdalar işlev değildir, işlev çağırma işlecine sahip nesnelerdir. Bu nedenle iki farklı lambda arasında aşırı yüklenme olmaz.

Artık, işlev nesneleriyle çalışmak için aşırı yük çözünürlüğü elde edebilirsiniz, ancak yalnızca tek bir nesne kapsamında. Ve eğer birden fazla varsa, aralarında operator()aşırı yük çözümü seçebilir.

Bununla birlikte, bir lambda'nın birden fazla sahip olmanın belirgin bir yolu yoktur operator(). Bize yardımcı olmak için basit ( ) bir yardımcı program sınıfı yazabiliriz :

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

ve bir kesinti kılavuzu:

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

bu ikisiyle, iki lambdayı aşırı yükleyebiliriz:

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

ve bitti.

Yazma overloadedhem de mümkündür ve ama daha çalışma gerektirir ve daha az zarif. Sorunun farkında olduğunuzda, derleyicinizin C ++ özellikleri açısından desteklediği özelliklerle eşleşen bir çözüm bulmak zor olmamalıdır.


Anladığım kadarıyla her "aşırı yüklenmiş" lamda'nın kendi yakalama bloğu var, yani bu lambdalar hiçbir şeyi paylaşmıyor (ve muhtemelen aynı verileri tekrar tekrar yakalayan CPU zamanını boşa harcıyor). Herhangi bir şans C ++ standart bunu düzeltmek için bir şey olacak? Veya çağrıları ayırmak için tek seçenek variadic generic lamda+ if constexpr?
CM

@CM Yığın taşması hakkında bir soru sormak için lütfen sağ üst taraftaki [Soru Ekle] düğmesine değil, [Yorum Ekle] düğmesine basın. Teşekkürler!
Yakk - Adam Nevraumont
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.