我是最近来自Swift的C ++新手。有没有办法得到更短的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..
};
有些列需要更长的lambdas,但因为编写lambda的语法与它自己的内容一样长。
lambda语法可以减少吗? (比如通过隐式参数或隐式返回最后一个语句)
例如在Swift中我可以做类似的事情,它会是一样的:
Col(_("Age"), "int", { $0.Age() }),
编辑:添加了Bank列作为更复杂的列的示例。
答案 0 :(得分:6)
如果您一直在调用会员功能,则可以使用mem_fn
:
Col(_("Name"), "string", std::mem_fn(&Person::Name)),
当传递指针或对象类型的引用时,mem_fn
有效。
答案 1 :(得分:5)
lambda语法可以减少吗?
我不这么认为。 lambda
功能的基本组件可满足您的需求:
[ capture-list ] ( params ) { body }
您可以使用C ++ 11中的最小格式。
答案 2 :(得分:2)
你应该返工Col_
而不是传递一个lambda,这样它就可以接受指向成员的指针,并知道如何处理它们(例如将它们包装在std::mem_fn
中)。那样你就可以写:
columns = {
Col(_("Name"), "string", &Person::Name),
Col(_("Age"), "int", &Person::Age),
//..etc..
};
答案 3 :(得分:1)
如果你有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..
};
答案 4 :(得分:1)
使用C ++ 14和宏,您可以从javascript(ES6)中模仿arrow functions的语法。
事实上,通常足以通过引用捕获所有内容。参数类型可以设置为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。
最后,我们可以使用this answer按参数的数量重载宏_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库对功能样式编程不友好......
完整代码可用on ideone。
答案 5 :(得分:0)
你可以#define
一个宏,但它有点hackish and evil。
#define GEN_LAMBDA(arg) [](Person *p){ return p->arg();}
columns = {
Col(_("Name"), "string", GEN_LAMBDA(Name)),
Col(_("Age"), "int", GEN_LAMBDA(Age)),
//..etc..
};
答案 6 :(得分:0)
您可以通过为每个数据成员定义策略来尝试基于策略的设计方法
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)
答案 7 :(得分:0)
我制作了一个 terse lambda library 来使用宏执行此操作,前提是您可以使用 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-friendly 和 noexcept
-friendly 适用于那些需要的情况。
请注意,TL
的 lambda 按值返回,就像您在示例 lambda 中所做的那样。如果您想要 lambda 的 decltype(auto)
返回类型,则可以改用 TLR
宏。
如果你想使用这个库,我建议谨慎;使用宏来改变语言的语法是一个危险的想法,可能会使您的代码难以阅读。