重载 lambda 函数
Overload a lambda function
如何重载一个简单的本地 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。
不,你不能超载 lambda!
lambda 是 anonymous functors(即未命名的函数对象),而不是简单的函数。因此,不可能重载这些对象。
你基本上想做的是 almost
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++ 中不能重复使用相同的变量名。
然而,在 c++17 we have if constexpr
中,可以实例化唯一在编译时为真的分支。
意味着可能的解决方案是:
- 单个 variabe template lambda。或
- 通用 lambda 并使用
decltype
查找参数类型
对于 if constexpr
检查。(学分 @NathanOliver)
使用 variabe template 你可以做类似的事情。 (在线观看现场演示)
#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]);
使用通用 lambda(自 c++14), the above will be: (See a live demo online)
#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]));
Lambda 基本上是局部定义的仿函数的语法糖。据我所知,它们永远不会被重载以使用不同的参数调用。请注意,每个 lambda 表达式都是不同的类型,因此即使抛开直接错误,您的代码也无法按预期工作。
但是,您可以定义一个带有重载 operator()
的仿函数。如果可能的话,这正是您从 lambdas 中得到的。你只是没有得到简洁的语法。
类似于:
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 不是函数,它们是带有函数调用运算符的对象。所以重载不能发生在两个不同的lambda之间。
现在,您可以获得重载解析以处理函数对象,但仅限于单个对象的范围内。然后如果有多个 operator()
,过载解决方案可以在它们之间进行选择。
然而,一个 lambda 没有明显的方法可以拥有多个 operator()
。我们可以编写一个简单的(在 c++17 中)实用程序 class 来帮助我们:
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]));
完成。
在 c++14 and c++11 中都可以编写 overloaded
,但需要更多的工作并且不够优雅。一旦您意识到这个问题,找到一个与您的特定编译器支持的 C++ 功能相匹配的解决方案应该不难。
可以使用boost/hana/functional/overload
来完成
#include <boost/hana/functional/overload.hpp>
#include <iostream>
#include <map>
void read()
{
static std::string line;
std::getline(std::cin, line);
auto translate = boost::hana::overload(
[](int idx) {
constexpr static int table[8]{ 7,6,5,4,3,2,1,0 };
return table[idx];
},
[](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;
}
如何重载一个简单的本地 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。
不,你不能超载 lambda!
lambda 是 anonymous functors(即未命名的函数对象),而不是简单的函数。因此,不可能重载这些对象。 你基本上想做的是 almost
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++ 中不能重复使用相同的变量名。
然而,在 c++17 we have if constexpr
中,可以实例化唯一在编译时为真的分支。
意味着可能的解决方案是:
- 单个 variabe template lambda。或
- 通用 lambda 并使用
decltype
查找参数类型 对于if constexpr
检查。(学分 @NathanOliver)
使用 variabe template 你可以做类似的事情。 (在线观看现场演示)
#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]);
使用通用 lambda(自 c++14), the above will be: (See a live demo online)
#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]));
Lambda 基本上是局部定义的仿函数的语法糖。据我所知,它们永远不会被重载以使用不同的参数调用。请注意,每个 lambda 表达式都是不同的类型,因此即使抛开直接错误,您的代码也无法按预期工作。
但是,您可以定义一个带有重载 operator()
的仿函数。如果可能的话,这正是您从 lambdas 中得到的。你只是没有得到简洁的语法。
类似于:
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 不是函数,它们是带有函数调用运算符的对象。所以重载不能发生在两个不同的lambda之间。
现在,您可以获得重载解析以处理函数对象,但仅限于单个对象的范围内。然后如果有多个 operator()
,过载解决方案可以在它们之间进行选择。
然而,一个 lambda 没有明显的方法可以拥有多个 operator()
。我们可以编写一个简单的(在 c++17 中)实用程序 class 来帮助我们:
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]));
完成。
在 c++14 and c++11 中都可以编写 overloaded
,但需要更多的工作并且不够优雅。一旦您意识到这个问题,找到一个与您的特定编译器支持的 C++ 功能相匹配的解决方案应该不难。
可以使用boost/hana/functional/overload
#include <boost/hana/functional/overload.hpp>
#include <iostream>
#include <map>
void read()
{
static std::string line;
std::getline(std::cin, line);
auto translate = boost::hana::overload(
[](int idx) {
constexpr static int table[8]{ 7,6,5,4,3,2,1,0 };
return table[idx];
},
[](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;
}