Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ specialize a lambda for a certain type [duplicate]

I was playing around with the lambda "overloading" as presented here, and quickly came to the point where I'd find it convenient to create specialized lambda closures. So I was first trial-and-error'ing a bit, with my most promising trial being

auto call_for_vector = [] template<typename T> (std::vector<T>) {};

However, a later look at the cppreference showed that neither this nor similar constructs seem to be allowed by the standard.

What is the reason why such specializations are not supported?

I know one can obtain this behaviour using SFINAE, but then it's less readable, harder to write and more error prone. Of course one can also simpy write a class with an appropriate operator(), but this is soo C++03 :-)



What use would be for such a syntax?:

One example, this would allow for an easy lambda "overload" like in the following code

template <class F1, class F2>
struct overload_set : F1, F2
{
    overload_set(F1 x1, F2 x2) : F1(x1), F2(x2) {}
    using F1::operator();
    using F2::operator();
};

template <class F1, class F2>
overload_set<F1,F2> overload(F1 x1, F2 x2)
{
    return overload_set<F1,F2>(x1,x2);
} 

auto f = overload(
         [](auto&& x){ std::cout<<"call by default"<<std::endl;},
         [] template<typename T>(std::vector<T>){std::cout<<"call for vector"<<std::endl;}
         );

One can obtain this behaviour e.g. by using SFINAE base on the technique in this answer, but again ... this sucks.

Is there an easy workaround to SFINAE for obtaining specific overloads?

like image 419
davidhigh Avatar asked Feb 14 '16 15:02

davidhigh


1 Answers

This type of coding may seem superfluous at first but there are some nice properties we can take advantage of. Regarding the post you mention, I was planning on a part 2, where I'd showcase a neat way of checking whether a type has a specific member (function or data). Say you want to check for a serialize member function; instead of using complicated mechanisms I discovered it gets as easy as :

auto hs = overload( 
    [ ](auto&& x) -> decltype(x.serialize(2), std::true_type{}) { 
        return{}; },       // ^^ this guy ^^ 
    [ ](...) -> std::false_type { return {}; }); 

demo

The specifics of standardese evolution can be found here, but the reason I posted this is to advocate for such a syntax which, had we have had it, the above could be extended to allow partial ordering among "overloaded" generic lambdas :

auto do_something = overload( 
    [ ]<class X>(shared_ptr<X> x) -> decltype(x.serialize(2), std::true_type{}) { 
        /*do something specific for shared ptrs of X */ return{}; },      
    [ ]<class X>(X& x) -> decltype(x.serialize(2), std::true_type{}) { 
        /*do a generic operation for other types that have serialize*/return{}; },      
    [ ](...) -> std::false_type { /*do nothing*/ return {}; }); 
like image 51
Nikos Athanasiou Avatar answered Oct 19 '22 22:10

Nikos Athanasiou