Лямбда как аргумент по умолчанию не работает

Я получаю сообщение об ошибке с последними версиями clang и gcc с этим кодом:

int main() {
auto lambda = [] (auto = [] {}) {};
lambda();
}

Clang выдает ошибку:

prog.cc: In function 'int main()':
prog.cc:3:12: error: no match for call to '(main()::<lambda(auto:1)>) ()'
lambda();
^
prog.cc:2:35: note: candidate: template<class auto:1> main()::<lambda(auto:1)>
auto lambda = [] (auto = [] {}) {};
^
prog.cc:2:35: note:   template argument deduction/substitution failed:
prog.cc:3:12: note:   couldn't deduce template parameter 'auto:1'
lambda();
^

Почему это не удается?

17

Решение

Тип вычета для auto не учитывает аргументы по умолчанию.

22

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

Так как лямбды сахар для функторов проблема в том, что функции шаблона не могут вывести аргументы шаблона (auto) в этом контексте по умолчанию.

Лямбда может быть уменьшена до уровня структуры функтора, принимая во внимание эти утверждения:

§5.1.2 / 3 [expr.prim.lambda]

Тип лямбда-выражения (который также является типом объекта замыкания) является уникальным, безымянным типом класса без объединения […]

§5.1.2 / 5 [expr.prim.lambda]

[…] Для обобщенной лямбды тип замыкания имеет открытый шаблон члена оператора вызова встроенной функции (14.5.2), чей список-параметра-шаблона состоит из одного изобретенного типа
Параметр шаблона для каждого вхождения auto в условии объявления-лямбды-параметра в порядке появления. […]

Таким образом, тип вашей лямбды эквивалентен этому типу функтора:

struct unnamed
{
template<typename Auto1>
auto operator()(Auto1 = []{})
{
}
};

И ваше использование тогда эквивалентно:

int main() {
auto lambda = unnamed();
lambda();
}

Тип Auto1 не может быть выведен в этом контексте, как указано в §14.8.2.5 / 5 [temp.deduct.type]:

Неведуемые контексты:

[…]

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

11

Шаблонные функции (или методы) не выводят свои параметры типа из своих аргументов по умолчанию, а замыкание с помощью auto Параметры это просто объект с шаблоном метода.

Это делает использование лямбды по умолчанию для функции шаблона немного раздражающим.

Один из подходов состоит в том, чтобы напечатать erase, вызывая объект, не сохраняя его, например:

#include <utility>
#include <type_traits>
#include <memory>

template<class Sig>
struct function_view;

template<class R, class...Args>
struct function_view<R(Args...)>{
void* state;
R(*f)(void*, Args&&...);

template<class F, class=std::enable_if_t<std::is_convertible<std::result_of_t<F&(Args...)>,R>{}>>
function_view( F&& fin ):
state(const_cast<void*>(static_cast<void*>(std::addressof(fin)))),
f( [](void* state, Args&&...args)->R{
F&& f = std::forward<F>(*static_cast<std::decay_t<F>*>(state));
return f(std::forward<Args>(args)...);
})
{}
function_view( R(*fin)(Args...) ):
state(fin),
f( fin?+[](void* state, Args&&...args)->R{
R(*f)(Args...) = static_cast<R(*)(Args...)>(state);
return f(std::forward<Args>(args)...);
}:nullptr)
{}
explicit operator bool(){return f;}
function_view():state(nullptr),f(nullptr){}
function_view(std::nullptr_t):function_view(){}
R operator()(Args...args)const{
return f(state, std::forward<Args>(args)...);
}
};
template<class...Args>
struct function_view<void(Args...)>{
void* state;
void(*f)(void*, Args&&...);

template<class F, class=std::result_of_t<F&(Args...)>>
function_view( F&& fin ):
state(const_cast<void*>(static_cast<void*>(std::addressof(fin)))),
f( [](void* state, Args&&...args){
F&& f = std::forward<F>(*static_cast<std::decay_t<F>*>(state));
f(std::forward<Args>(args)...);
})
{}
function_view( void(*fin)(Args...) ):
state(fin),
f( fin?+[](void* state, Args&&...args){
void(*f)(Args...) = static_cast<void(*)(Args...)>(state);
f(std::forward<Args>(args)...);
}:nullptr)
{}

explicit operator bool(){return f;}
function_view():state(nullptr),f(nullptr){}
function_view(std::nullptr_t):function_view(){}
void operator()(Args...args)const{
f(state, std::forward<Args>(args)...);
}
};

int main() {
auto f = [] (function_view<void()> x=[]{}) {
x();
};
f();
}

Так как это работает только с указателями на функции, и у меня был хороший опыт работы со встроенными в gcc указателями на простые указатели функций, это может не так сильно повлиять на производительность, как std::function, И в отличие от std::function нет виртуальных таблиц или выделения кучи.

живой пример

Для не лямбда, вы можете сделать это:

template<class X=function_view<void()>>
void f( X&& x=[]{} ) {
x();
}

который выводит, если вы передаете, является аргументом, и превращается в пустую функцию, если вы этого не сделаете. Вы также можете сделать:

struct do_nothing {
template<class...Args>
void operator()(Args&&...)const{}
};

template<class X=do_nothing>
void f( X&& x=do_nothing{} ) {
x();
}

что может быть проще оптимизировать.

2
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector