C ++ лямбды с эллипсами в списке параметров - PullRequest
0 голосов
/ 10 мая 2018

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

(Другими словами, я реализую аналог C ++ "call \ fresh" от miniKanren.)

Поскольку пользователь может захотеть ввести любое число от нуля до множества свежих переменных в конкретной области, я хочу, чтобы пользователь мог передавать в библиотеку лямбда-выражения с различным количеством аргументов. Однако я не знаю ни одного (простого) способа (в C ++ 14) определить число параметров для произвольного лямбда-объекта.

Мне пришла в голову мысль, почему бы не передать фиксированное число (скажем, 10) аргументов идентификатора переменной в лямбду, и чтобы код пользователя использовал эллипсы в лямбде, чтобы игнорировать ненужные? Примерно так:

auto no_args = call_fresh([](...) { return success(); });
auto one_arg = call_fresh([](var A, ...) { return A == 1; });
auto two_args = call_fresh([](var A, var B, ...) { return A == 1 && B == 2; });

Кажется, что проводник компилятора принимает эллипсы в списках лямбда-параметров, по крайней мере, с помощью gcc.

Это будет называться примерно так (обратите внимание, что код всегда передает 10 идентификаторов переменных независимо от того, называет ли «f» только один, два или ни одного из них):

template <typename F>
auto call_fresh(F f)
{
   return [f](StateCounter sc) {
      return f(sc+0,sc+1,sc+2,sc+3,sc+4,
          sc+5,sc+6,sc+7,sc+8,sc+9);
   };
}

Разумеется, я удивился, что существует такая особенность, есть ли причина не использовать лямбды с эллипсами?

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

Ваши лямбды - это, по сути, C-стиль функции переменной . Нет ничего неправильного с их использованием, и если вы не хотите получать доступ к значениям (что несколько уродливо), это нормально.

Однако, основная проблема, которую, похоже, вы действительно хотите решить, состоит в том, чтобы позволить вашей библиотеке найти количество аргументов (или arity ) функции / lambda /. .., что вы можете сделать с помощью шаблонного метапрограммирования - пользователям не нужно обходить эту проблему.

Раскрытие: есть реализация этого в библиотеке, над которой я также работаю, здесь .

Вот простой пример:

template <typename Callable>
struct function_arity : public function_arity<decltype(&Callable::operator())>
{};

template <typename ClassType, typename ReturnType, typename... Args>
struct function_arity<ReturnType(ClassType::*)(Args...) const>
{
    constexpr static size_t arity = sizeof...(Args);
};

template <typename ClassType, typename ReturnType, typename... Args>
struct function_arity<ReturnType(ClassType::*)(Args...)>
{
    constexpr static size_t arity = sizeof...(Args);
};

Компилятор автоматически определит типы аргументов для вас, а sizeof... даст вам необходимое вам количество аргументов.

Затем вы можете использовать function_arity<decltype(lambda)>::arity, чтобы получить количество аргументов вашей лямбды. Последняя версия имеет дело с mutable лямбдами, где оператор вызова не является константой. Вы также можете захотеть расширить это для правильной работы с noexcept, или вы столкнетесь с ошибками вроде this libc ++ bug .

К сожалению, не будет работать с перегруженными или шаблонными operator() (например, если вы используете параметры типа auto в своей лямбде). Если вы также хотите поддерживать функции вместо лямбд, могут потребоваться дополнительные специализации.

0 голосов
/ 10 мая 2018

Однако я не знаю ни одного (простого) способа (в C ++ 14) определить количество параметров для произвольного лямбда-объекта.

Мне кажется, что вы ищете sizeof...() по варианту auto списка параметров

#include <iostream>

int main ()
 {
   auto l = [](auto ... as) { return sizeof...(as); };

   std::cout << l(1, 2L, 3.0, 4.0f, "5") << std::endl; // print 5
 }
...