Подтвердить что ты не робот

Рекурсивный вызов в лямбда (С++ 11)

Возможный дубликат:
Рекурсивные лямбда-функции в С++ 0x

Почему я не могу назвать лямбда рекурсивно, если я напишу его как:

auto a = [&]
{ 
   static int i = 0; i++;
   std::cout << i << std::endl; 
   if (i<10) 
     a(); //recursive call
};

Он дает ошибку компиляции (ideone):

prog.cpp:8:18: error: '((const main()::<lambda()>*)this)->main()::<lambda()>::a' cannot be used as a function

prog.cpp: In function 'int main()':
prog.cpp:9:9: error: variable 'auto a' with 'auto' type used in its own initializer

Что означает ошибка?

Я понимаю причину, по которой я не могу написать это:

auto i=i+1; //error: unable to deduce 'auto' from '<expression error>'

Мы не можем записать это, потому что из него следует выводить тип i, что означает, что тип не может быть выведен, если сама строка i появляется в инициализации (ideone). Но как это важно в случае лямбда? Если я не ошибаюсь, тип лямбда определяется его параметрами (-ами) и типом возврата; он не зависит от тела, если он ничего не возвращает (в этом случае тип возврата выводится как void, независимо от других утверждений в лямбда-теле).

Во всяком случае, у меня есть обходной путь, и я могу использовать std::function вместо:

std::function<void()> a = [&] 
{ 
   static int i = 0; i++;
   std::cout << i << std::endl; 
   if (i<10) 
      a();
};

которые компилируют штрафы (ideone). Но мне все еще интересно узнать, почему версия auto не компилируется.

4b9b3361

Ответ 1

Причина в том, что для инициализаторов лямбда-выражения нет переменных auto.

Такие особые случаи были бы склонны к ошибкам и злоупотреблениям. Вам нужно определить правила, когда вы предлагаете работать что-то вроде a(). Как выглядит operator()? Каково точное состояние типа a? Будет ли тип завершен? (это означает, что вы уже знаете список захвата лямбда). Как только вы сформулируете, что в формате, разумном для спецификации, было бы легче сделать на нем инструкции.

Разрешение вашего варианта использования будет означать еще один случай, когда вам нужно сканировать вперед в коде, потому что для определения типа a в a() вы должны быть уверены, что инициализатор заканчивается ничем, что могло бы "разблокировать" Тип

struct y { void operator()() { } };
template<typename T> y operator+(T, y) { return y(); }
auto x = [] { x(); } + y();

В этом случае x() будет вызывать y::operator(), а не лямбда.

Как и сейчас, a просто запрещается упоминаться во всем его инициализаторе. Потому что в С++ auto не является типом. Это просто спецификатор типа, стоящий для того, чтобы быть выведенным типом. Как следствие, выражение никогда не может иметь тип auto.

Ответ 2

Как я вижу, важная разница между случаем auto a и std::function<void()> a заключается в том, что тип std::function<void()> не знает/не заботится о том, каков тип реальной функции, на которую он ссылается. Запись:

std::function<void()> a;

отлично, где as:

auto a;

мало смысла. Поэтому, когда пришло время синтезировать захват, если вы используете std::function<void()>, все, что должно быть известно о типе, уже известно, тогда как auto оно еще не известно.

Ответ 3

В рекурсивной функции f определяется f, а тип возврата f также определяется f в случае auto, что приводит к бесконечной рекурсии.

когда auto пытается получить тип. decltype (f()) будет далее выводить на другой параметр decltype (f) `, поскольку f имеет значение f e.g. вызов любого рекурсивного рекурсивного тоже. определение возвращаемого типа становится рекурсивным при применении к рекурсивной функции. в рекурсивной функции конец рекурсии может выполняться во время выполнения. но определение статично только