Рекурсивный вызов в лямбда (С++ 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
не компилируется.
Ответы
Ответ 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. вызов любого рекурсивного рекурсивного тоже. определение возвращаемого типа становится рекурсивным при применении к рекурсивной функции. в рекурсивной функции конец рекурсии может выполняться во время выполнения. но определение статично только