Достижение чистых лямбда-функций в C ++ 11 - PullRequest
9 голосов
/ 21 октября 2011

Я много играл с новыми лямбда-кодами C ++ 11, и требование полного указания аргумента шаблона является настоящим тормозом. Синтаксис, который я бы хотел бы использовать , выглядит примерно так:

#include <vector>
#include <algorithm>

struct foo
{
    void bar() {}
};

int main()
{
    vector<foo> v(10);

    for_each(v.begin(), v.end(), [](f) {f.bar();});
                                   ^^^
}

Есть ли способ приблизить что-нибудь к этому? Библиотека Boost с Phoenix в порядке, но синтаксис для вызова функций-членов требует лотов пластины котла - я полагаю, что после C ++ 11 простота вызова функций-членов в сочетании с автоматическим выводом Phoenix типа. *

Текущая идея

Я объяснил это синтаксисом:

vector<foo> x(1);
vector<bar> y(1);
for_each(x.begin(), x.end(), [](_a f) {f->f();});
for_each(y.begin(), y.end(), [](_a b) {b->b();});

Что работает, но вы должны добавить возможность для каждого типа (например, ADD_AUTO_LAMBDA_SUPPORT(foo);). Он также имеет ограничение на то, что все поддерживаемые типы не могут иметь неоднозначных членов.

Полный код для этого:

#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

struct foo
{
    foo() : x(3) {}
    int x;
    void f() { cout << x << endl;}
};

struct bar
{
    bar() : y(133.7) {}
    double y;
    void b() { cout << y << endl;}
};

struct combo : foo, bar { };

struct _a
{
    _a(foo& f) : offset(reinterpret_cast<combo*>(&f)) {}
    _a(bar& b) : offset(reinterpret_cast<combo*>((char*)&b - 2*sizeof(foo))) {}

    combo* operator->() { return offset; }

private:
    combo* offset;
};

int main()
{
    vector<foo> x(1);
    vector<bar> y(1);

    for_each(x.begin(), x.end(), [](_a f) {f->f();});
    for_each(y.begin(), y.end(), [](_a b) {b->b();});
}

Затем вы могли бы использовать магию шаблона и препроцессора для генерации _a и combo, но проблема возникает, когда у вас есть неоднозначные имена (например, третья структура с функцией b() - вам нужен способ устраните неоднозначность их, о которых я не могу думать в настоящее время.

Ответы [ 6 ]

13 голосов
/ 21 октября 2011

Примечание: я полностью согласен, что [](auto f){ ... } было бы очень желательно!

Пока у нас этого нет, а как насчет старого доброго typedef? Он просто добавляет одну строку, очень «низкотехнологичен» и облегчает чтение лямбды:

typedef const map<key_type, value_type>::value_type&  λp_t;
for_each(m.begin(), m.end(), [&](λp_t x) {...});
2 голосов
/ 21 октября 2011

Итак, предположительно в случае следующего:

std::array<int,10> a;
for_each(begin(a),end(a),[](auto i){ /* ... */ });

Вы хотите, чтобы компилятор выяснил, что лямбда-выражение принимает целое число, и в основном читает это как:

for_each(begin(a),end(a),[](int i){ /* ... */ });

Проблема в том, что тип лямбды влияет на тип создания шаблона for_each, который может выбрать другую специализацию, что, в свою очередь, может потребовать другого вывода типа для параметра lambda. Поэтому для компилятора просто нет разумного способа использовать код алгоритма для автоматического определения типа передаваемых аргументов.

В любом случае, по крайней мере, для алгоритма for_each это вам не нужно, просто используйте диапазон для цикла:

for(auto i:a) { /* ... */ }

А в других местах используйте decltype:

transform(begin(a),end(a),begin(a),[](decltype(*begin(a)) i) { return 2*i; });
2 голосов
/ 21 октября 2011

Вы можете использовать decltype:

for_each(m.begin(), m.end(), [&](decltype(*m.begin()) x){...});

Но это действительно, действительно, отстой, что вы не можете использовать auto в анонимных лямбдах, и это заставляет меня думать, что весь комитет по c ++ - это кучка идиотов, которые не работают с c ++ ежедневно.

Обновление:

Вы могли бы также сделать

#define _A(CONTAINER_NAME) decltype(*CONTAINER_NAME.begin())
for_each(m.begin(), m.end(), [&](_A(m) x) { ... });
1 голос
/ 14 января 2012

Вы можете иметь лямбда-выражение для вывода параметров (и типов) из функции, но тогда каждая функция должна сделать это доступным. Здесь обсуждается, как это можно сделать здесь:

http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/c347334187628476?hl=en

По сути, вы можете вызывать функции по-рубиновому. Таким образом, вы бы назвали for_each так:

$(for_each(some_range), x)
{
    printf("%i\n", x);
};

Знак доллара ($) - это макрос, который выполняет вывод типа из функции. Теперь это не будет работать с std::for_each, его нужно использовать со специально определенной функцией, которая выдает типы параметров для лямбда-выражения.

Поскольку он использует макрос, он не учитывает приоритет оператора, поэтому его нельзя использовать с адаптерами диапазона.

0 голосов
/ 16 октября 2013

Так что это компилируется

#define FOR_EACH_IN(iter__, list__, ...) \
    std::for_each(list__.begin(), list__.end(), [&](decltype(*list__.begin()) iter__) __VA_ARGS__)

Дано:

auto DoSomethingTo = [](T AnElement){ /* insert profitable activity */ };
std::array<T, n> AnArray;

Вы можете сделать:

FOR_EACH_IN(AnElement, AnArray,
{
    DoSomethingTo(AnElement);
});

Фанатик JavaScript внутри меня взволнован тем, как он выглядит, но как программист на C ++, я ужасаюсь, что придумал что-то подобное. Это может привести к отключению большого количества статических анализов и линтеров, а также, если что-то пойдет не так, надеюсь, вам понравится отладка макросов.

Но, эй, это чисто.

0 голосов
/ 21 октября 2011

Если вы готовы использовать макросы и использовать все эти настройки, не будет ли достаточно быстрого ярлыка с использованием decltype? Что-то вроде:

#define T_FOR_EACH( begin_, end_, var_, func_ ) \
  for_each( (begin_), (end_), [](decltype(*begin_) var_) func_ )

Тогда вы могли бы иметь:

T_FOR_EACH(x.begin(), x.end(), &f, {f->f();} );

Причина, по которой я не ставлю & для переменной в определении, заключается в том, что с этим форматом можно будет по-прежнему указывать все нужные вам спецификаторы типа, а также ссылку или копию.

Извините, если синтаксис неправильный, у меня нет компилятора C ++ 11, с которым я могу протестировать все это, это всего лишь идея.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...