C++ template deduction from lambda

C++ template deduction from lambda



I have a function which takes two std::functions as arguments. The parameter of the second function has the same type as the result of the first.


std::function



I wrote a function template like this:


template<typename ResultType>
void examplFunction(std::function<ResultType()> func, std::function<void(ResultType)> func2)
auto x = func();
func2(x);



I can call it with:


void f()
examplFunction<int>(() return 1; , //
(int v) std::cout << "result is " << v << std::endl; );



Is there a way to to get rid of the <int> at examplFunction<int> and let the compiler deduce the type of ResultType?


<int>


examplFunction<int>


ResultType






Lambdas are not std::functions. std::functions are type erasure tools. Type deduction and type erasure are opposites of each other. If you are deducing the type of a type erasure tool you are almost always screwing up.

– Yakk - Adam Nevraumont
Sep 7 '18 at 15:38


std::function


std::function




4 Answers
4



Do you actually need std::function in there? std::function is useful when you need type erasure. With templates, you can usually skip it altogether:


std::function


std::function


template<class F1, class F2>
void examplFunction(F1 func, F2 func2, decltype(func2(func()))* sfinae = nullptr)
auto x = func();
func2(x);



The sfinae parameter makes sure the function can only be called with functions such that func2 can be called with the result of func.


sfinae


func2


func






Note that your SFINAE is not actually corresponding to the implementation. If func returned a move-only type (say, std::unique_ptr) then the SFINAE would allow it but the function body wouldn't compile. Example.

– Max Langhof
Sep 7 '18 at 15:27



func


std::unique_ptr






@MaxLanghof Yes, but the exact same issue applies to the OP's original code. The SFINAE is only there to guarantee what the OP asked for.

– Angew
Sep 7 '18 at 15:29






True I guess. I'm left to wonder if this can somehow be broken with implicit conversions (and an overloaded func2)... But I don't see a way.

– Max Langhof
Sep 7 '18 at 15:31



func2






Hmm it seems to do what it should. But the decltype(func2(func()))* sfinae = nullptr looks very magical to me. How can a third function argument restrict the first and second?

– ErWu
Sep 7 '18 at 16:19



decltype(func2(func()))* sfinae = nullptr






@ErWu en.wikipedia.org/wiki/SFINAE Basically, if func2(func()) would be ill-formed, it prevents the template from being instantiated.

– Angew
Sep 7 '18 at 16:34


func2(func())



Yes, there is.


template<typename ResultType>
void examplFunction_impl(std::function<ResultType()> func, std::function<void(ResultType)> func2)
auto x = func();
func2(x);


template<class F1, class F2>
void examplFunction(F1&& f1, F2&& f2)

using ResultType = decltype(f1());
examplFunction_impl<ResultType>(std::forward<F1>(f1), std::forward<F2>(f2));



Demo



In this case you require that f1 be invocable with no arguments, so you can figure out the return type in the helper function. Then you call the real function while explicitly specifying that return type.


f1



You could add some SFINAE to make sure this function only participates in overload resolution when f1 can indeed be invoked like that (and if f2 can also be invoked with the return value of f1).


f1


f2


f1



Although I have to agree with @Angew that in the given example there is no need for std::function. That might of course be different in a real-world situation.


std::function






Also nice, thank you.

– ErWu
Sep 7 '18 at 16:29



std::function has a templated (and otherwise unconstrained) constructor, so deducing it from simply an argument type is not that easy a deal. If those arguments still need to be std::functions, you can skip one <int> for the price of two std::functions, and let deduction guides do the rest:


std::function


std::function


<int>


std::function


void f()
examplFunction(std::function(() return 1; ), //
std::function((int v) std::cout << "result is "
<< v << std::endl; ));



A fun fact is that this does not always work. For instance, the current implementation of libc++ lacks guides for std::function, thus violating the standard.


std::function



Angew's answer is great (and should be the accepted answer), but is missing the minor detail of checking that the section function doesn't return anything. To do that you'll need to use the std::is_void type trait, and std::enable_if:


std::is_void


template<class F1, class F2>
void examplFunction(F1 func, F2 func2, std::enable_if_t<std::is_void_v<decltype(func2(func()))>, void*> sfinae = nullptr)
auto x = func();
func2(x);



This obvious is more verbose and difficult to read if you aren't familiar with type traits and SFINAE, so it probably isn't the best way forward if you don't need to make sure F2 returns void.


void






The OP's original code doesn't require that func2 return void. std::function<void(ResultType)> is constructible-from a function that returns something; it'll just cast the return value to void, which is to say, it'll ignore it. std::function<void()>( () return 42; ) compiles just fine.

– Quuxplusone
Sep 8 '18 at 5:50


func2


void


std::function<void(ResultType)>


void


std::function<void()>( () return 42; )



Thanks for contributing an answer to Stack Overflow!



But avoid



To learn more, see our tips on writing great answers.



Required, but never shown



Required, but never shown




By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Crossroads (UK TV series)

ữḛḳṊẴ ẋ,Ẩṙ,ỹḛẪẠứụỿṞṦ,Ṉẍừ,ứ Ị,Ḵ,ṏ ṇỪḎḰṰọửḊ ṾḨḮữẑỶṑỗḮṣṉẃ Ữẩụ,ṓ,ḹẕḪḫỞṿḭ ỒṱṨẁṋṜ ḅẈ ṉ ứṀḱṑỒḵ,ḏ,ḊḖỹẊ Ẻḷổ,ṥ ẔḲẪụḣể Ṱ ḭỏựẶ Ồ Ṩ,ẂḿṡḾồ ỗṗṡịṞẤḵṽẃ ṸḒẄẘ,ủẞẵṦṟầṓế