C ++更短的lambda语法

时间:2016-04-21 16:15:06

标签: c++ c++11 lambda

我是最近来自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列作为更复杂的列的示例。

8 个答案:

答案 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 宏。


如果你想使用这个库,我建议谨慎;使用宏来改变语言的语法是一个危险的想法,可能会使您的代码难以阅读。