Jak stworzyć zmienną generyczną lambdę?

Od C++14 możemy używać ogólnych lambdów:

auto generic_lambda = [] (auto param) {};

Oznacza to, że operator wywołania jest wzorowany na parametrach oznaczonych jako auto.

Pytanie brzmi, jak stworzyć lambda, która może przyjmować zmienną liczbę parametrów, podobnie jak będzie działał zmienny szablon funkcji ? Jeśli nie jest to możliwe, jaka jest najbliższa rzecz, która może być używana w ten sam sposób ? Jak byś to przechowywał ? Czy jest to możliwe w std::function ?

Author: Drax, 2014-09-17

3 answers

Nie jestem pewien, jaki jest Twój zamiar, ale zamiast przechowywać go w std::function możesz użyć samej lambdy, aby uchwycić params. Jest to przykład omawiany na liście dyskusyjnej boost. Jest używany w implementacji boost:: hana

auto list = [](auto ...xs) {
    return [=](auto access) { return access(xs...); };
};

auto head = [](auto xs) {
    return xs([](auto first, auto ...rest) { return first; });
};

auto tail = [](auto xs) {
    return xs([](auto first, auto ...rest) { return list(rest...); });
};

auto length = [](auto xs) {
    return xs([](auto ...z) { return sizeof...(z); });
};

// etc...
// then use it like

auto three = length(list(1, '2', "3")); 
 49
Author: mkaes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-09-20 09:36:09

Składnia

Jak stworzyć zmienną lambdę generyczną ?

Możesz utworzyć zmienną generyczną lambda o następującej składni:

auto variadic_generic_lambda = [] (auto... param) {};

W zasadzie wystarczy dodać ... pomiędzy auto (ewentualnie ref) a nazwą Twojego parametru.

Więc typowo używając uniwersalnych odniesień dałoby:

auto variadic_generic_lambda = [] (auto&&... param) {};

Użycie

Jak korzystać z parametrów ?

Należy wziąć pod uwagę zmienny parametr ogólny jako posiadanie parametru szablonu typu pack, ponieważ tak jest. To mniej więcej oznacza, że większość, jeśli nie wszystkie, użycie tych parametrów będzie wymagało szablonów w ten czy inny sposób.

Oto typowy przykład:

#include <iostream>

void print(void)
{
}

template <typename First, typename ...Rest>
void print(const First& first, Rest&&... Args)
{
  std::cout << first << std::endl;
  print(Args...);
}

int     main(void)
{
  auto variadic_generic_lambda = [] (auto... param)
    {
      print(param...);
    };

  variadic_generic_lambda(42, "lol", 4.3);
}

Przechowywanie

Jak przechowywać zmienną generyczną lambdę ?

Możesz użyć auto do przechowywania lambda w zmiennej własnego typu, lub możesz zapisać ją w std::function ale będziesz mógł zadzwonić tylko z poprawiony podpis pod tym std::function:

auto variadic_generic_lambda = [] (auto... param) {};

std::function<void(int, int)> func = variadic_generic_lambda;

func(42, 42); // Compiles

func("lol"); // Doesn't compile

A co z kolekcjami różnych rodzajów lambda ?

Ponieważ każda lambda ma inny typ, nie można przechowywać ich bezpośredniego typu w zwykłych jednorodnych pojemnikach stl. Sposób, w jaki robi się to z innymi rodzajowymi lambdami, polega na przechowywaniu ich w odpowiedniej std::function, która będzie miała stałe wywołanie sygnatury i nie będzie niczego powstrzymywać, ponieważ twoja lambda nie jest generyczna w pierwszej kolejności i może być wywołany w ten sposób:

auto non_generic_lambda_1 = [] (int, char) {};
auto non_generic_lambda_2 = [] (int, char) {};

std::vector<std::function<void(int, char)>> vec;

vec.push_back(non_generic_lambda_1);
vec.push_back(non_generic_lambda_2);

Jak wyjaśniono w pierwszej części tej sekcji storage jeśli możesz ograniczyć się do danej stałej sygnatury wywołania, możesz zrobić to samo z zmienną generyczną lambda.

Jeśli nie możesz, będziesz potrzebował jakiejś formy heterogenicznego kontenera, takiej jak:

  • std::vector<boost::variant>
  • std::vector<boost::any>
  • boost::fusion::vector

Zobacz to pytanie dla przykładu heterogenicznego pojemnika.

Co jeszcze ?

Aby dowiedzieć się więcej na temat lambda oraz szczegółów na temat generowanych prętów i jak korzystać z parametrów w lambda, zobacz: [19]}
 40
Author: Drax,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:24

Rozważ to

#include <iostream>

    namespace {

        auto out_ = [] ( const auto & val_) 
        {
            std::cout << val_;
            return out_ ;
        };

        auto print = [](auto first_param, auto... params)
        {
            out_(first_param);

            // if there are  more params
            if constexpr (sizeof...(params) > 0) {
                // recurse
                print(params...);
            }
                return print;
        };
    }

int main()
{
    print("Hello ")("from ")("GCC ")(__VERSION__)(" !"); 
}

(wandbox tutaj ) to "Drukuj" lambda to:

  • Variadic
  • rekurencyjne
  • ogólne
  • Fast

I żadnych szablonów w zasięgu wzroku. (tuż pod spodem:)) Brak kodu C++ wyglądającego jak szum radiowy. Proste, czyste i co najważniejsze:

  • Łatwy w utrzymaniu

Nic dziwnego ,że "czuje się jak nowy język".

 5
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-28 10:01:11