โอเวอร์โหลดฟังก์ชั่นแลมบ์ดา


14

วิธีโอเวอร์โหลดฟังก์ชั่นแลมบ์ดาแบบง่าย ๆ ในท้องถิ่น?

SSE ของปัญหาดั้งเดิม:

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

ข้อความผิดพลาด

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

โปรดอย่ารังเกียจที่จะไม่ตรวจสอบอินพุตของผู้ใช้นี่คือ SSE


7
แลมบ์ดาไม่ใช่ฟังก์ชั่นมันเป็นวัตถุดังนั้นการบรรทุกเกินพิกัดจะไม่นำมาใช้กับพวกเขา translateเป็นตัวแปรเฉพาะที่ไม่สามารถใช้ชื่อเดียวกันซ้ำได้
user7860670

2
เกี่ยวข้อง / dupe: stackoverflow.com/questions/32475576/…
NathanOliver

คำตอบ:


10

ไม่คุณไม่สามารถบรรทุกแลมบ์ดาได้!

lambdas เป็นหน้าที่ไม่ระบุชื่อ (เช่นฟังก์ชันวัตถุที่ไม่มีชื่อ) และไม่ใช่ฟังก์ชั่นทั่วไป ดังนั้นการโอเวอร์โหลดวัตถุเหล่านั้นจึงเป็นไปไม่ได้ สิ่งที่คุณพยายามจะทำก็คือเกือบ

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

ซึ่งเป็นไปไม่ได้เนื่องจากชื่อตัวแปรเดียวกันไม่สามารถนำกลับมาใช้ใหม่ได้ใน C ++


อย่างไรก็ตามในเรามีif constexprที่หนึ่งสามารถยกตัวอย่างสาขาเดียวที่เป็นจริงในเวลารวบรวม

ความหมายของคำตอบที่เป็นไปได้คือ:

  • แลมบ์ดาแม่แบบตัวแปรเดี่ยว หรือ
  • แลมบ์ดาทั่วไปและค้นหาประเภทของพารามิเตอร์ที่ใช้decltype สำหรับif constexprตรวจสอบ (เครดิต@NathanOliver )

การใช้เทมเพลต Variabeคุณสามารถทำสิ่งต่างๆได้ ( ดูตัวอย่างสดออนไลน์ )

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

และเรียกว่าชอบ

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

ใช้แลมบ์ดาทั่วไป (ตั้งแต่ ) สิ่งที่กล่าวมาข้างต้นจะเป็น: ( ดูการสาธิตสดออนไลน์ )

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

และเรียกแลมบ์ดาอย่างที่คุณทำตอนนี้:

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

3
ฉันพบสิ่งที่น่าอัศจรรย์นี้
snoopy

1
ครั้งแรกของคุณความต้องการที่จะเป็นelse if else if constexprประการที่สองทำไมต้องใช้แม่แบบตัวแปร คุณสามารถสร้างแลมบ์ดาทั่วไปและการสอนของคุณจะกลายเป็นif constexpr (std::is_same_v<decltype(idx), int>)และelse if constexpr (std::is_same_v<decltype(idx), char>)
NathanOliver

6

แลมบ์ดาเป็นน้ำตาลซินแทคติกโดยทั่วไปสำหรับหน้าที่ที่กำหนดไว้ในเครื่อง เท่าที่ฉันรู้พวกมันไม่เคยถูกเรียกให้รับภาระมากเกินไปจนถูกเรียกด้วยพารามิเตอร์ที่แตกต่างกัน โปรดทราบว่านิพจน์แลมบ์ดาทุกประเภทแตกต่างกันดังนั้นแม้จะเกิดข้อผิดพลาดทันทีรหัสของคุณก็ไม่สามารถทำงานได้ตามที่ตั้งใจไว้

แต่คุณสามารถกำหนด functor operator()ที่มีมากเกินไป นี่จะเป็นสิ่งที่คุณต้องการจากลูกแกะถ้าเป็นไปได้ คุณไม่ได้รับไวยากรณ์สั้น ๆ

สิ่งที่ต้องการ:

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

เดี๋ยวก่อนคุณกำลังโทรหาแลมบ์ดาไวยากรณ์
user7860670

1
@VTT เป็นเรื่องดีที่ไวยากรณ์สั้น เมื่อเปรียบเทียบกับของโบราณบางอย่างมันก็ไม่เลวร้ายนัก
idclev 463035818

5

ดังนั้นกฎสำหรับการโอเวอร์โหลดชื่อจะใช้กับการค้นหาชื่อฟังก์ชันบางประเภทเท่านั้น (ทั้งฟรีและเมธอด)

แลมบ์ดาไม่ใช่ฟังก์ชั่น แต่เป็นวัตถุที่มีตัวดำเนินการเรียกฟังก์ชัน ดังนั้นการบรรทุกเกินพิกัดจึงไม่สามารถเกิดขึ้นได้ระหว่างลูกแกะสองตัวที่แตกต่างกัน

ตอนนี้คุณสามารถรับความละเอียดเกินพิกัดเพื่อทำงานกับวัตถุฟังก์ชัน แต่ภายในขอบเขตของวัตถุเดียวเท่านั้น และถ้ามีมากกว่าหนึ่งการแก้ไขการoperator()โอเวอร์โหลดสามารถเลือกระหว่างกันได้

แลมบ์ดา operator()แต่ยังไม่มีวิธีที่ชัดเจนที่จะมีมากกว่าหนึ่ง เราสามารถเขียนคลาสยูทิลิตี้ง่ายๆ (ใน ) เพื่อช่วยเรา:

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

และคู่มือการหัก:

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

ด้วยสองสิ่งนี้เราสามารถบรรทุกลูกแกะสองตัวได้:

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

และทำ

การเขียนoverloadedเป็นไปได้ทั้งและแต่ต้องใช้งานมากขึ้นและสง่างามน้อยลง เมื่อคุณตระหนักถึงปัญหาแล้วให้หาวิธีการแก้ปัญหาที่ตรงกับสิ่งที่คอมไพเลอร์ของคุณรองรับในลักษณะของ C ++ ไม่น่าจะยาก


ตามที่ฉันเข้าใจแลมด้า "โอเวอร์โหลด" แต่ละบล็อกมีบล็อกการจับภาพของตัวเองนั่นคือแลมบ์ดาเหล่านั้นไม่ได้แชร์อะไรเลย (และอาจเสียเวลา CPU ในการเก็บข้อมูลเดิมซ้ำ ๆ ) มีโอกาส C ++ มาตรฐานจะมีสิ่งที่จะแก้ไขหรือไม่ หรือตัวเลือกเดียวคือvariadic generic lamda+ if constexprเพื่อแยกสาย?
CM

@CM หากต้องการถามคำถามเกี่ยวกับการล้นสแต็กโปรดกดปุ่ม [ถามคำถาม] ที่ด้านบนขวาไม่ใช่ปุ่ม [เพิ่มความคิดเห็น] ขอบคุณ!
Yakk - Adam Nevraumont
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.