Я думаю, что следующий код очень удобен и не вреден:
auto fn = [](bool b = false) -> int // NOT legal in C++11
{
return b ? 1 : 0;
};
Почему C ++ 11 явно запрещает аргументы по умолчанию для лямбда-выражения?
Мне просто интересно обоснование и соображения позади.
Я хочу знать «ПОЧЕМУ», а не «ЧТО» в стандарте C ++ 11.
Нет реальной причины, по которой лямбды не могут иметь аргументы по умолчанию. Тем не менее, есть два основных способа использования лямбд, и только один из них допускает использование аргументов по умолчанию без изменения системы типов.
Вы можете позвонить в лямбду напрямую или через шаблон. Параметры по умолчанию будет работать нормально в этом случае.
Вы можете позвонить в лямбду через std::function
, Параметры по умолчанию не будут работать без изменения системы типов.
Я предполагаю, что новые функции, которые люди пишут на C ++ 11, обычно принимать std::function
параметры, потому что, таким образом, функция не будет иметь быть шаблоном или его не нужно создавать для каждой лямбды и функтора, которые ему передают.
std::function
(или указатель на функцию) принимает значения по умолчанию?Не очевидно, какой будет тип такой функции.
Если это std::function<int(bool)>
тогда как вы называете это по умолчанию? (Вы не можете.)
Если это std::function<int(bool=false)>
, с какими типами он совместим и как работает конвертация? Можете ли вы преобразовать его в std::function<int()>
? Как насчет std::function<int(bool=true)>
?
Если это что-то новое, как std::function<int(bool=default)>
, затем с какими типами он совместим и как работает конвертация?
По сути, это не просто переключатель, который вы можете включить в стандарт и сделать указатели на функции / std::function
обрабатывать аргументы по умолчанию. Аргументы по умолчанию в обычных функциях обрабатываются с использованием информации из объявления функции, которая недоступна на сайте вызова для лямбды или указателя функции. Таким образом, вам придется закодировать информацию о значениях по умолчанию в тип функции, а затем выработать все неочевидные правила для преобразования и совместимости.
Таким образом, вам придется придумать убедительные аргументы в пользу Зачем такая функция будет добавлена, и убедит комитет.
Я не ответил на этот вопрос. Но я не думаю, что это была бы очень полезная возможность для добавления. Я бы удалил этот ответ, если бы мог, но он был принят. Я бы понизил это, если бы мог, но это мое. Такова жизнь.
Как показывают комментарии к вопросу, вероятно, нет технический причина для того, чтобы не было никаких аргументов по умолчанию. Но обратный вопрос: «Есть ли практическое причина аргументов по умолчанию? »Я думаю, что ответом будет« нет », и вот почему.
Чтобы вызвать лямбду, вы можете позвонить прямо сейчас.
[] { printf("foo"); }();
Я уверен, что это имеет ограниченное использование, если таковые имеются, так что давайте двигаться дальше. Единственный другой способ вызвать лямбду — это сначала связать ее с переменной, и здесь есть несколько вариантов.
auto foo = [] { printf("foo"); }; foo();
void (*foo)() = [] { printf("foo"); }; foo()
, Конечно, это работает, только если лямбда не захватывает.Теперь давайте рассмотрим полезность аргументов по умолчанию в каждом случае.
Я думаю, что этих моментов достаточно, чтобы сказать, что аргументы по умолчанию для лямбды действительно не так полезны. Кроме того, я вижу, что некоторые люди говорят о проблемах с типом лямбды, если у него есть аргументы по умолчанию, но это не проблема IMO. Вы всегда можете написать свой собственный функтор, который делает то же самое, и который делает есть аргумент по умолчанию. Кроме того, о снижении до указателя на функцию также сказать особо нечего. Возьми нормальную функцию
void func(int i = 0)
{
}
и возьми его адрес. Что вы получаете? void (*)(int)
, Нет причин, по которым лямбда следовала бы другим правилам.
Я согласен, что нет реального «технического» ограничения как таковой разрешить работу аргументов по умолчанию в лямбдах в некоторых случаях. Это не испортит ваши указатели и auto
потому что тип функции не зависит от аргументов по умолчанию. Но это также, почему это не было бы очень практично.
Зачем?
Поскольку аргументы по умолчанию, хотя и являются частью сигнатуры функции, не являются частью типа функции:
[C++11: 1.3.17]:
подпись
<функция> имя, список типов параметров (8.3.5) и окружающее пространство имен (если есть)
[ Замечания: Подписи используются в качестве основы для искажения имени и связывания. —Конечная записка ]
[C++11: 8.3.5/6]:
[..] Тип возврата, Параметр типа-лист, реф-классификатор, и резюме-спецификатор-сл, но не аргументы по умолчанию (8.3.6) или спецификация исключения (15.4), являются частью типа функции. [ Замечания: Типы функций проверяются во время присваивания и инициализации указателей на функции, ссылок на функции и указателей на функции-члены. —Конечная записка ]
По сути, они представляют собой кусочек синтаксического сахара, который «активируется» компилятором, который может видеть объявление используемой вами функции, и вводится в точке вызова функции:
#include <iostream>
void foo(int x = 5)
{
std::cout << x << '\n';
}
int main()
{
foo();
}
5
Аргумент по умолчанию «видимый».
Однако, когда вы «прячете» свою функцию за указателем:
int main()
{
void (*bar)(int) = &foo;
bar();
}
too few arguments to function
Тип bar
правильно, и компилятор знает, что foo
имеет значение по умолчанию, но там просто нет непосредственный синтаксис, существующий для информирования компилятора о точке вызова bar
тот bar
это также foo
, Конечно, в этом тривиальном сценарии это можно выяснить, соблюдая назначение, но вряд ли это оправдывает более широкий аргумент.
По той же причине аргументы по умолчанию, указанные только в определении, невидимом на сайте вызовов, почти бесполезны:
// a.h
void foo(int);
// a.cpp
#include "a.h"#include <iostream>
void foo(int x = 5)
{
std::cout << x << '\n';
}
// main.cpp
#include "a.h"
int main()
{
foo();
}
too few arguments to function
Я полагаю, что это является причиной для:
[C++11: 8.3.6/4]:
[..] Объявления в разных областях имеют совершенно разные наборы аргументов по умолчанию. [..]
Мы являются разрешено «накапливать» аргументы по умолчанию для определений функций-членов класса без шаблонов ([C++11 8.3.6/6]
); пример показывает, что это значение по умолчанию будет применяться только в том же TU, что соответствует поведению, которое мы видели выше в моем втором фрагменте кода.
Таким образом, если аргументы по умолчанию не являются частью типа функции и должны быть однозначно видимыми для сайта вызова, то существует лишь несколько простых тривиальных угловых случаев, в которых они будут полезны для лямбда-выражений, то есть когда они вызываются в той же области, в которой они созданы, так что компилятор может легко найти способ «заполнить» аргумент по умолчанию для вызова лямбды и, ну, в чем смысл? Не много, говорю тебе.