Что если лямбда-выражение C ++ 11 поддерживает аргументы по умолчанию?

Я думаю, что следующий код очень удобен и не вреден:

auto fn = [](bool b = false) -> int // NOT legal in C++11
{
return b ? 1 : 0;
};

Почему C ++ 11 явно запрещает аргументы по умолчанию для лямбда-выражения?

Мне просто интересно обоснование и соображения позади.

Я хочу знать «ПОЧЕМУ», а не «ЧТО» в стандарте C ++ 11.

26

Решение

Нет реальной причины, по которой лямбды не могут иметь аргументы по умолчанию. Тем не менее, есть два основных способа использования лямбд, и только один из них допускает использование аргументов по умолчанию без изменения системы типов.

  1. Вы можете позвонить в лямбду напрямую или через шаблон. Параметры по умолчанию будет работать нормально в этом случае.

  2. Вы можете позвонить в лямбду через 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 обрабатывать аргументы по умолчанию. Аргументы по умолчанию в обычных функциях обрабатываются с использованием информации из объявления функции, которая недоступна на сайте вызова для лямбды или указателя функции. Таким образом, вам придется закодировать информацию о значениях по умолчанию в тип функции, а затем выработать все неочевидные правила для преобразования и совместимости.

Таким образом, вам придется придумать убедительные аргументы в пользу Зачем такая функция будет добавлена, и убедит комитет.

Итак, почему лямбды не могут принимать значения по умолчанию?

Я не ответил на этот вопрос. Но я не думаю, что это была бы очень полезная возможность для добавления. Я бы удалил этот ответ, если бы мог, но он был принят. Я бы понизил это, если бы мог, но это мое. Такова жизнь.

17

Другие решения

Как показывают комментарии к вопросу, вероятно, нет технический причина для того, чтобы не было никаких аргументов по умолчанию. Но обратный вопрос: «Есть ли практическое причина аргументов по умолчанию? »Я думаю, что ответом будет« нет », и вот почему.

Чтобы вызвать лямбду, вы можете позвонить прямо сейчас.

[] { printf("foo"); }();

Я уверен, что это имеет ограниченное использование, если таковые имеются, так что давайте двигаться дальше. Единственный другой способ вызвать лямбду — это сначала связать ее с переменной, и здесь есть несколько вариантов.

  1. Используйте авто. Итак, мы получаем auto foo = [] { printf("foo"); }; foo();
  2. Свяжите это с указателем на функцию: void (*foo)() = [] { printf("foo"); }; foo(), Конечно, это работает, только если лямбда не захватывает.
  3. Сделайте эквивалент 1 или 2, передав лямбду функции или шаблону функции.

Теперь давайте рассмотрим полезность аргументов по умолчанию в каждом случае.

  1. В этом случае мы вызываем лямбду напрямую, поэтому код, вероятно, достаточно сплочен, и мы не будем вызывать лямбда с различным количеством аргументов. Если это так, лямбда может (должна?), Вероятно, быть преобразована в более общий компонент. Я не вижу здесь никакой практической пользы.
  2. (см. 1)
  3. Мы передаем лямбду в другую функцию. Я не вижу здесь практической пользы для аргумента по умолчанию. Вспомните старые добрые функторы (которые могут иметь аргументы по умолчанию) — я не могу сказать, что знаю слишком много функций, которые рассчитывают на наличие аргументов по умолчанию, даже для этих. Поскольку лямбды являются просто функторами, нет никаких причин для внезапного изменения этого наблюдения.

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

void func(int i = 0)
{
}

и возьми его адрес. Что вы получаете? void (*)(int), Нет причин, по которым лямбда следовала бы другим правилам.

3

Я согласен, что нет реального «технического» ограничения как таковой разрешить работу аргументов по умолчанию в лямбдах в некоторых случаях. Это не испортит ваши указатели и 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();
}
  • Ошибка в main.cpp: too few arguments to function

Я полагаю, что это является причиной для:

[C++11: 8.3.6/4]: [..] Объявления в разных областях имеют совершенно разные наборы аргументов по умолчанию. [..]

Мы являются разрешено «накапливать» аргументы по умолчанию для определений функций-членов класса без шаблонов ([C++11 8.3.6/6]); пример показывает, что это значение по умолчанию будет применяться только в том же TU, что соответствует поведению, которое мы видели выше в моем втором фрагменте кода.

Таким образом, если аргументы по умолчанию не являются частью типа функции и должны быть однозначно видимыми для сайта вызова, то существует лишь несколько простых тривиальных угловых случаев, в которых они будут полезны для лямбда-выражений, то есть когда они вызываются в той же области, в которой они созданы, так что компилятор может легко найти способ «заполнить» аргумент по умолчанию для вызова лямбды и, ну, в чем смысл? Не много, говорю тебе.

3
По вопросам рекламы [email protected]