Лямбды — это функциональные объекты под капотом. Общие лямбда-выражения — это функциональные объекты с шаблоном operator()
s.
template<class...Fs>
struct funcs_t{};
template<class F0, class...Fs>
struct funcs_t<F0, Fs...>: F0, funcs_t<Fs...> {
funcs_t(F0 f0, Fs... fs):
F0(std::move(f0)),
funcs_t<Fs...>(std::move(fs)...)
{}
using F0::operator();
using funcs_t<Fs...>::operator();
};
template<class F>
struct funcs_t<F>:F {
funcs_t(F f):F(std::move(f)){};
using F::operator();
};
template<class...Fs>
funcs_t< std::decay_t<Fs>... > funcs(Fs&&...fs) {
return {std::forward<Fs>(fs)...};
}
auto f_all = funcs( f1, f2 )
генерирует объект, который является перегрузкой как f1
, так и f2
.
auto g_integral =
[](auto&& func, auto&& param1, auto&&... params)
-> std::enable_if_t< std::is_integral<
std::decay_t<decltype(param1)>
>{}>
{
// ...
};
auto g_not_integral =
[](auto&& func, auto&& param1, auto&&... params)
-> std::enable_if_t< !std::is_integral<
std::decay_t<decltype(param1)>
>{}>
{
// ...
};
auto gL = funcs( g_not_integral, g_integral );
и вызов gL
выполнит разрешение перегрузки SFINAE для двух лямбда-выражений.
Вышеприведенное делает некоторые ложные ходы, которых можно было бы избежать при линейном наследовании funcs_t
. В библиотеке промышленного качества я мог бы сделать наследование бинарным, а не линейным (чтобы ограничить глубину реализации шаблонов и глубину дерева наследования).
Кроме того, я знаю 4 причины, по которым SFINAE включает лямбда-выражения.
Во-первых, с новым std::function
вы можете перегружать функцию несколькими разными сигнатурами обратного вызова.
Во-вторых, описанный выше трюк.
В-третьих, каррирование объекта функции, где он оценивает, имеет ли он правильное количество и тип аргументов.
Далее, автоматическая распаковка кортежей и тому подобное. Если я использую стиль передачи продолжения, я могу спросить у переданного в продолжении, примет ли он распакованный кортеж или будущий разобранный и т. д.
person
Yakk - Adam Nevraumont
schedule
06.07.2015
void
. Проблема возникает, когда вам нужно временно сохранить возвращаемое значение, сделать что-то еще, а затем вернуть его (или ничего не возвращать в случае типа возвратаvoid
). В этом случае вы, вероятно, можете обойти это, создав оболочку RAII, которая выполняет часть do something else в своем деструкторе. Или отправка тегов другим лямбда-выражениям/функциям на основеresult_of
. - person Praetorian   schedule 06.07.2015Timer t; return f(args...);
иTimer::~Timer
печатает результат. - person Kerrek SB   schedule 06.07.2015func
, возвращающее некотороеtimer
, которое назначеноk
- person Ryan Haining   schedule 06.07.2015void
. Чего вы не можете сделать, так это предоставить дополнительную версию, которая работает только для возвращаемого типаvoid
. - person Praetorian   schedule 06.07.2015