C++较短的 lambda 语法
C++ shorter lambda syntax
我是C++新手,最近来自 Swift。有没有办法获得更短的 lambda 语法?
我有很多台词,例如:
columns = {
Col(_("Name"), "string", [] (Person *p) {
return p->Name();
}),
Col(_("Age"), "int", [] (Person *p) {
return p->Age();
}),
Col(_("Bank"), "string", [&banks] (Person *p) {
return banks.lookUp(p->Identifier()).Name;
}),
//..etc..
};
某些列需要更长的 lambda,但由于它是编写 lambda 的语法,因此与它本身的内容一样长。
lambda 语法可以减少吗?(通过隐式参数或隐式返回最后一个语句)
例如,在 Swift 中,我可以做这样的事情,它会是一样的:
Col(_("Age"), "int", { $0.Age() }),
编辑:添加了"银行"列作为更复杂的列的示例。
如果始终调用成员函数,则可以使用 mem_fn
Col(_("Name"), "string", std::mem_fn(&Person::Name)),
mem_fn
在传递指向对象类型的指针或引用时起作用。
lambda 语法可以减少吗?
我不这么认为。满足您需求的lambda
功能的基本组件包括:
[ capture-list ] ( params ) { body }
您可以使用 C++11 尽可能以最小的形式使用它。
使用 C++14 和宏,您可以非常模仿 javascript (ES6) 中箭头函数的语法。
实际上,通常通过引用捕获所有内容就足够了。参数类型可以设置为 auto,返回类型可以省略。因此,C++14 允许我们的最短版本是:
auto f = [&](auto x, auto y) { return x + y; };
显然,它可以很容易地转换为宏:
#define _L2(a, b, res) [&](auto a, auto b) { return res; }
#define _S2(res) _L2(_0, _1, res)
_L2
宏创建具有两个用户指定参数的 lambda。_S2
宏使用名为 _0
和 _1
的参数创建一个 lambda。最后,我们可以使用此答案按参数数重载宏_L
。我不知道如何通过宏推断出_S
的参数数量。
现在我们可以写这样的东西:
auto g = _L(x, y, x + y); //std::plus
auto g = _S2(_0 + _1); //std::plus
你甚至可以做一些疯狂的事情,比如:
//turns unary accessor function into binary comparator functor
auto compByKey = _L(f, _L(x, y, f(x) < f(y)));
//sort vector<string> by length
sort(arr.begin(), arr.end(), compByKey(_L(x, x.length())));
事实上,语法仍然不像原始的javascript或swift那样清晰,但它要短得多。现在的问题是记住我们定义的所有种类的 lambda =)无论如何,STL库对函数式编程并不友好...
完整的代码可在 ideone 上找到。
与其传递 lambda,不如重新设计Col_
,以便它可以接受指向成员的指针并知道如何处理它们(例如,将它们包装在 std::mem_fn
中)。这样你就可以写:
columns = {
Col(_("Name"), "string", &Person::Name),
Col(_("Age"), "int", &Person::Age),
//..etc..
};
如果你有 C++14(如果你来自 swift,你可能会这样做),那么你可以用 auto 替换参数类型。此外,非捕获 lambda 不需要空间(它们等效于函数指针),因此如果您只是预定义它们并在初始化器中使用副本,则不会对性能造成影响。
// this will return the Name of any pointee that has a Name() method
auto get_name = [](auto*p) { return p->Name(); }
auto get_age = [](auto*p) { return p->Age(); }
columns = {
Col(_("Name"), "string", get_name),
Col(_("Age"), "int", get_age),
//..etc..
};
我制作了一个简洁的lambda库来使用宏来做到这一点,前提是您可以使用C++20(v0.1.1支持C++17,但有痛苦的警告)。使用此库,您的代码将是:
columns = {
Col(_("Name"), "string", [] TL(_1->Name())),
Col(_("Age"), "int", [] TL(_1->Age())),
Col(_("Bank"), "string", [&banks] TL(banks.lookUp(_1->Identifier()))),
//..etc..
};
这个TL
宏为您提供了一个类似于 { $0.Age() }
Swift 语法的表达式 lambda,允许您使用 _1
、_2
等访问参数。此外,对于需要的情况,此lambda对SFINAE友好且noexcept
友好。
请注意,TL
的 lambda 按值返回,就像您在示例 lambda 中所做的那样。如果您希望 lambda 具有decltype(auto)
返回类型,则可以改用TLR
宏。
如果你想使用这个库,我建议谨慎;使用宏来改变语言的语法是一个危险的想法,可能会使你的代码难以阅读。
为此#define
一个宏,但它有点黑客和邪恶。
#define GEN_LAMBDA(arg) [](Person *p){ return p->arg();}
columns = {
Col(_("Name"), "string", GEN_LAMBDA(Name)),
Col(_("Age"), "int", GEN_LAMBDA(Age)),
//..etc..
};
您可以通过为每个数据成员定义策略来尝试基于策略的设计方法
enum FieldID {AGE, NAME, ID};
template<FieldID fid> auto get(Person &p);
template<FieldID fid> std::string getType();
template<FieldID fid> std::string getFieldName();
template <> auto get<AGE>(Person &p) {return p.Age();}
template <> auto get<NAME>(Person &p) {return p.Name();}
template <> auto get<ID>(Person &p) {return p.ID();}
template <> std::string getType<AGE>() {return "int";}
template <> std::string getType<NAME>() {return "string";}
template <> std::string getType<ID>() {return "size_t";}
template <> std::string getFieldName<AGE>() {return "Age";}
template <> std::string getFieldName<NAME>() {return "Name";}
template <> std::string getFieldName<ID>() {return "Bank";}
你的代码将看起来像这样
Col(_(getFieldName<AGE>()), getType<AGE>(), get<AGE>(p)
前面的答案中有惊人的版本,我有一个不太令人印象深刻的解决方案,但它很简单并且对我来说效果很好(c ++ 20):
// language: c++
#define LambdaBody(...)
noexcept(noexcept(__VA_ARGS__)) ->decltype(auto)
requires requires { __VA_ARGS__; }
{
return __VA_ARGS__;
}
#define Lambda0(...)
<typename T = void>() LambdaBody(__VA_ARGS__)
#define Lambda1(_1, ...)
([[maybe_unused]] auto&& _1) LambdaBody(__VA_ARGS__)
#define Lambda2(_1, _2, ...)
([[maybe_unused]] auto&& _1,
[[maybe_unused]] auto&& _2) LambdaBody(__VA_ARGS__)
#define Lambda3(_1, _2, _3, ...)
([[maybe_unused]] auto&& _1,
[[maybe_unused]] auto&& _2,
[[maybe_unused]] auto&& _3) LambdaBody(__VA_ARGS__)
#define Lambda4(_1, _2, _3, _4, ...)
([[maybe_unused]] auto&& _1,
[[maybe_unused]] auto&& _2,
[[maybe_unused]] auto&& _3,
[[maybe_unused]] auto&& _4) LambdaBody(__VA_ARGS__)
// ----------------------------------------------------------
#include <iostream>
int main(int i, char ** argv) {
auto context = 0;
auto demo0 = [&] Lambda0(++context);
auto demo1 = [] Lambda1(x, x + 10);
auto demo2 = [] Lambda2(x, y, x*y + 10);
auto demo3 = [] Lambda3(x, y, z, x*y + z);
std::cout << "demo0 = " << demo0() << "n";
std::cout << "demo0 = " << demo0() << "n";
std::cout << "demo1 = " << demo1(5) << "n";
std::cout << "demo2 = " << demo2(1, 2) << "n";
std::cout << "demo3 = " << demo3(1, 2, 3) << "n";
}
请参阅编译器资源管理器:https://compiler-explorer.com/z/Gc5aofY6v
- 1d 智能指针不适用于语法 (*)++
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 助记符和指向成员语法的指针
- 有人能分解一下这个c++模板的语法吗
- C++ lambda 和运算符语法
- 结构化绑定语法是否可以在多态 lambda 中使用
- 模板类内 lambda 函数上的 __declspec(noinline) 在 VS2017 中抛出语法错误
- 在Qt信号和插槽中使用lambda语法并访问传递的参数
- 是否可以避免使用lambda中的尾随返回型语法
- 是否可以通过从lambda的引用中返回T型对象,而无需使用尾随返回类型语法
- 在C 11 /14语法中,是否有可能编写lambda函数,该函数将看到父变量
- 语法繁重的多重lambda包装器的替代方案-如何避免样板代码
- C++较短的 lambda 语法
- 在 C++11 lambda 语法中,堆分配的闭包
- 在具有类似 lambda 语法的结构列表中搜索 C++
- lambda表达式语法错误
- Lambda 语法或 gcc 错误的最后一分钟更改
- 如何摆脱lambda语法
- 是否允许(或将允许)在lambda表达式中使用熟悉的模板语法
- Qt5 信号/时隙语法,带过载信号和 lambda