重载lambda函数


14

如何重载简单的本地lambda函数?

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
Lambda不是函数,它们是对象,因此重载永远不会应用于它们。translate只是不能重复使用相同名称的局部变量。
user7860670

Answers:


10

不,您不能让lambda超载!

Lambda是匿名函子(即未命名的函数对象),而不是简单的函数。因此,不可能使那些对象过载。您基本上想做的几乎是

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通过它实例化在编译时为true的唯一分支。

意味着可能的解决方案是:

  • 单个variabe模板 lambda。要么
  • 通用lambda并查找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];
    }
};

然后像现在一样调用lambda:

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

Lambda基本上是局部定义函子的语法糖。据我所知,它们永远不会被重载以使用不同的参数来调用。请注意,每个lambda表达式都具有不同的类型,因此即使撇开即时错误,您的代码也无法按预期工作。

但是,您可以定义带有重载的函子operator()。如果可能的话,这将是您从lambda上获得的确切结果。您只是没有简洁的语法。

就像是:

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

请稍等,您正在调用lambda语法好吗?
user7860670

1
@VTT语法很简洁。与一些更古老的东西相比,
还算

5

因此,重载名称的规则仅适用于某些类型的函数名称查找(自由和方法)。

Lambda不是函数,它们是带有函数调用运算符的对象。因此,在两个不同的lambda之间不会发生重载。

现在,您可以获得重载解析以使用功能对象,但仅在单个对象的范围内。然后,如果有多个operator(),则可以在它们之间进行重载解决。

但是,lambda没有明显的方法可以拥有多个lambda operator()。我们可以编写一个简单的(在)实用程序类来帮助我们:

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

以及推论指南:

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

使用这两个,我们可以重载两个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]));

并做了。

overloaded中都可以进行编写,但是需要更多的工作并且不够优雅。一旦意识到了问题,找到与您的特定编译器支持的C ++功能相匹配的解决方案就不难了。


据我了解,每个“重载”的lamda都有其自己的捕获块,即那些lambda不共享任何内容(并且可能浪费CPU时间一遍又一遍地捕获相同的数据)。C ++标准有什么机会纠正这一点吗?还是唯一的选择是variadic generic lamda+ if constexpr来分开通话?
厘米

@CM要在堆栈溢出时提出问题,请按右上方的[Ask Question]按钮,而不是[Add Comment]按钮。谢谢!
Yakk-Adam Nevraumont
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.