람다 함수 오버로드


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

답변:


10

아니요, 람다에 과부하가 걸리지 않습니다!

람다는 익명 펑 (즉, 익명 함수 객체), 그리고 간단한 기능을합니다. 따라서 해당 객체를 오버로드 할 수 없습니다. 당신이 기본적으로하려고하는 것은 거의

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하는 일이 컴파일시에 사실 수있는 유일한 지점을 인스턴스화 할 수 있습니다.

가능한 해결책은 다음과 같습니다.

  • 단일 variabe 템플릿 람다. 또는
  • 일반적인 람다 및 사용하여 매개 변수의 유형을 찾을 수 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
이 놀라운 발견
스누피

1
먼저, 귀하 else if는이어야 else if constexpr합니다. 둘째, 왜 변수 템플릿을 사용합니까? 당신은 일반적인 람다를 만들 수와 checls이 될 것입니다 if constexpr (std::is_same_v<decltype(idx), int>)else if constexpr (std::is_same_v<decltype(idx), char>)
NathanOliver

6

Lambdas는 기본적으로 로컬로 정의 된 functors의 구문 설탕입니다. 내가 아는 한, 다른 매개 변수로 호출되도록 과부하되지 않았습니다. 모든 람다 식은 다른 유형이므로 즉각적인 오류를 제외하고는 코드가 의도 한대로 작동하지 않습니다.

그러나 과부하가있는 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.