HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Functional composition of member function predicates

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
predicatesfunctionmemberfunctionalcomposition

Problem

In C++11, it is en vogue to use small lambda expressions to produce ad-hoc predicates. However, sometimes it's fun to create predicates in a functional, "point-free" way. That way, no function bodies need to be inspected, the code is mildly self-documenting, and it's a great icebreaker at parties.

Suppose I have a std::vector v, and I want to remove all the empty inner vectors. The lambda-style remove_if call might look like this:

std::remove_if(v.begin(),
               v.end(),
               [](std::vector const & w) -> bool { return w.empty(); });


This is verbose and redundant. The point-free approach uses std::mem_fn:

std::remove_if(v.begin(),
               v.end(),
               std::mem_fn(&std::vector::empty));


This works fine. But now the problem: Suppose I want to further compose this predicate, say by negating it. In the lambda this would be a trivial change. But for the functional notation, we would like to use the library function std::not1 to produce a unary_negate wrapper.

However, the obvious std::not1(std::mem_fn(&std::vector::empty)) does not compile. The problem appears to be that the result type of std::mem_fn defines its argument_type member type as a pointer to the class, not a reference; cf. 20.8.10/2:


The simple call wrapper shall define two nested types named argument_type and result_type as synonyms for cv T* and Ret, respectively, when pm is a pointer to member function with cv-qualifier cv and taking no arguments, where Ret is pm’s return type.

This breaks the composability with std::not1.

I have written this type mangling work-around which strips the unwanted pointer:

#include 

template 
struct result_type_mangler : T
{
    using argument_type = typename std::remove_pointer::type;
    result_type_mangler(T t) : T(t) { }
};

template 
result_type_mangler mangle(T t)
{
    return result_type_mangler(t);
}


Now I can compose the predicates:

```
std::not1(mangle(std::me

Solution

The verbosity of C++14 generic lambdas should be much lower than std::mem_fun

std::remove_if(v.begin(),
               v.end(),
               [](auto const & w) { return w.empty(); });


If you want to remove non-empty vectors, you can also do something like

std::function)> is_empty = [](auto const & w) { return w.empty(); }; 
std::remove_if(v.begin(),
               v.end(),
               std::not1(is_empty));


Oh and the -> bool in your question is already superfluous in C++11 for single-line lambdas. Generic lambdas are currently supported by Clang >= 3.4, GCC 4.9 and MSVC 2013 November CTP.

I think Scott Meyers even has an Item "Prefer lambdas over bind" in his upcoming book Effective C++11/14.

Code Snippets

std::remove_if(v.begin(),
               v.end(),
               [](auto const & w) { return w.empty(); });
std::function<bool(std::vector<int>)> is_empty = [](auto const & w) { return w.empty(); }; 
std::remove_if(v.begin(),
               v.end(),
               std::not1(is_empty));

Context

StackExchange Code Review Q#37917, answer score: 3

Revisions (0)

No revisions yet.