Вывод типа лямбда выражения c параметром по умолчанию

1,00
р.
Без использования опции -Wpedantic компилятор с поддержкой c++11 разрешает использовать в лямбда-выражениях значение по умолчанию.
Для такого выражения можно использовать тип std::function с указанием типов всех параметров, в том числе и тех, что указаны по умолчанию.
Однако, используя автоматический вывод типов с указанием ключевого слова auto, лямбда выражение получает специфичный тип, например, main()::, где значения в круглых скобках - типы параметров, передаваемых в это выражение. И эти значения указываются всегда вне зависимости от того, есть ли у них значение по умолчанию (что в общем-то и логично).
Ниже привожу пример кода.
#include #include
void f1(std::function lambda) { if (lambda) { std::cout << lambda() << std::endl } }<br>void f2(std::function lambda) { if (lambda) { std::cout << lambda(1) << std::endl } }<br>int main() { auto lambda1 = [](int a = 3) -> int { return a }
f1(lambda1) f2(lambda1)
std::function lambda2 = []() { return 1 }
std::function lambda3 = [](int a) { return a }
f1(lambda2) //f2(lambda2) // Очевидно несоответствие типов
//f1(lambda3) // Очевидно несоответствие типов f2(lambda3) }
В примере кода тип main():: приводится к std::function. Мне не хватает знаний для понимания, почему компилятор не ругается на несоответствие типов. Прошу помощи в объяснении.
И связанные вопросы.
Могу ли я явно указать тип, подобный тому, что выводится автоматически?
Могу ли я указать компилятору, чтобы он запрещал подобное приведение типов?

Ответ
Тип объекта замыкания (closure object), порождаемого лямбда-выражением, никак не зависит от параметров лямбды и не определяется ими. Наличие или отсутствие аргументов по умолчанию здесь никакой роли не играет. Каждое лямбда-выражение порождает новый уникальный тип - это все, что вам нужно знать о типе объекта замыкания. Даже если два лямбда-выражения полностью идентичны, они все равно получат никак не связанные друг с другом уникальные типы. То, что в вашем примере во внутреннем имени этого типа вы увидели какой-то намек на список параметров - не более чем косметическая особенность используемой вами реализации. При этом то текстовое описание типа объекта замыкания, который вы видите в сообщениях компилятора, не является исчерпывающим описанием фактического типа.
Например, в ответ на
auto f1 = [](){} auto f2 = [](){} &f1 == &f2
вы получите от GCC диагностическое сообщение вида
error: comparison between distinct pointer types 'main()::*' and 'main()::*' lacks a cast
Как видите, описания типов в кавычках совершенно идентичны. Тем не менее это разные, никак не связанные друг с другом типы.
Так что при работе с лямбда-выражениями никакой речи о "соответствии типов" не может быть вообще: все типы объектов замыкания уникальны и никогда ничему не соответствуют.
почему компилятор не ругается на несоответствие типов
Класс std::function<> построен на принципах type erasure, т.е. фактически является более узкоспециализированным аналогом std::any. Конструктор этого класса является шаблонным (см. номер 5 здесь) - в нем вообще нет и быть не может никакой проверки "соответствия типов" на уровне базовой системы типов языка. На уровне базовой системы типов языка этот конструктор может принимать на вход абсолютно любые типы.
А какие-либо проверки навешиваются на этот конструктор уже на уровне библиотечной реализации, через техники вроде SFINAE. При инициализации вида std::function sf = f проверяется требование Callable: объект f должен быть Callable в качестве функции типа F. Проверка этого требования и работает в вашем примере.
Например, объект замыкания lambda1 является Callable и как int() и как int(int). Поэтому вы можете использовать его и для инициализации std::function, и для инициализации std::function.
Могу ли я явно указать тип, подобный тому, что выводится автоматически?
Явно - нет. Тип объекта замыкания определяется реализацией и синтаксиса для его явного указания не существует. "Указать" его вы можете только неявно после того, как он уже вывелся - через применение decltype к уже существующему объекту замыкания
auto f1 = [](){} decltype(f1) f2 = f1
Могу ли я указать компилятору, чтобы он запрещал подобное приведение типов?
Компилятор здесь ни при чем. Вы имеете дело с чисто библиотечной функциональностью: реализацией std::function<>. Пока вы пользуетесь классом std::function<>, он будет вести себя как того требует спецификация языка.