3

I've written this functor to perform an and operation (&&):

// unary functor; performs '&&'
template <typename T>
struct AND
{
    function<bool (const T&)> x;
    function<bool (const T&)> y;

    AND(function<bool (const T&)> xx, function<bool (const T&)> yy) 
             : x(xx), y(yy) {}
    bool operator() ( const T &arg ) { return x(arg) && y(arg); }
};

// helper
template <typename T>
AND<T> And(function<bool (const T&)> xx, function<bool (const T&)> yy)
{
    return AND<T>(xx,yy);
}

Note it's constructor argument types: function<bool (const T&)>.

Now, I am trying to instantiate it in various ways (inside big_odd_exists()):

int is_odd(int n) { return n%2; }
int is_big(int n) { return n>5; }


bool big_odd_exists( vector<int>::iterator first, vector<int>::iterator last ) 
{
    function<bool (const int &)> fun1 = is_odd;
    function<bool (const int &)> fun2 = is_big;

    return any_of( first, last, And( fun1, fun2 ) );  // instantiating an And object
}

int main()
{
    std::vector<int> n = {1, 3, 5, 7, 9, 10, 11};

    cout << "exists : " << big_odd_exists( n.begin(), n.end() ) << endl;
}

To my surprise, none of the implicit instantiations of the std::functions would compile.

Here are the cases I've tried (g++-4.8):

This compiles (explicit instantiation of a std::function object):

function<bool (const int &)> fun1 = is_odd;
function<bool (const int &)> fun2 = is_big;

return any_of( first, last, And( fun1, fun2 ) );

This does not compile (implicit instantiation of a temporary std::function object):

return any_of( first, last, And( is_odd, is_big ) );   // error: no matching function for call to ‘And(int (&)(int), int (&)(int))’

This compiles (explicit instantiation of a std::function object):

function<bool (const int &)> fun1 = bind(is_odd,_1);
function<bool (const int &)> fun2 = bind(is_big,_1);

return any_of( first, last, And(fun1, fun2) );

This does not compile (implicit instantiation of a temporary std::function object):

return any_of( first, last, And(bind(is_odd,_1), bind(is_big,_1)) );  // error: no matching function for call to ‘And(std::_Bind_helper<false, int (&)(int), const std::_Placeholder<1>&>::type, std::_Bind_helper<false, int (&)(int), const std::_Placeholder<1>&>::type)’

As far as I understand, the std::functions do not have explicit constructors. So, why can't I use the nicer to read versions of the calls?

I have all the test cases in: http://coliru.stacked-crooked.com/a/ded6cad4cab07541

Grim Fandango
  • 2,296
  • 1
  • 19
  • 27

2 Answers2

6

The problem is that there's no way to deduce T in this context. Let's look at it from the compiler's perspective:

template <typename T>
AND<T> And(function<bool (const T&)> xx, function<bool (const T&)> yy)

// Later, invoked as:
And( is_odd, is_big )

"Hm, no T is specified on the call, I will have to deduce it. What's the argument? is_odd, which is decayed to type int (*)(int). Now, I would have to instantiate std::function<bool (const T&)> for all possible T values and see for which/if any it can be constructed from type int (*)(int). Well, there's infinitely many of them. Not gonna do this."

It would work if you specified the T tempalte argument explicitly:

return any_of( first, last, And<int>( is_odd, is_big ) );

Live example


Note that this holds even if you changed is_odd and is_big to match the function signature exactly (returning bool and taking const int&). It's the deduction that's the problem.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
2

I suggest not abusing std::function and taking normal generic parameters instead. Treat std::function as a type-erased container for callable objects of a particular signature (see https://stackoverflow.com/a/11629125/46642 for more details).

// unary functor; performs '&&'
template <typename X, typename Y>
struct AND
{
    X x;
    Y y;

    template <typename XX, typename YY>
    AND(XX&& xx, YY&& yy) 
             : x(std::forward<XX>(xx)), y(std::forward<YY>(yy)) {}

    template <typename T>
    auto operator() ( const T &arg ) -> decltype(x(arg) && y(arg))
    { return x(arg) && y(arg); }
};

template <typename T>
using Decay = typename std::decay<X>::type;

// helper
template <typename X, typename Y>
AND<Decay<X>, Decay<Y>> And(X&& xx, Y&& yy)
{
    return AND<Decay<X>, Decay<Y>>(std::forward<X>(xx), std::forward<Y>(yy));
}
Community
  • 1
  • 1
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
  • @GrimFandango your comment `// won't compile` is... wrong? As can be seen in the page you linked to, it compiles and runs properly. – R. Martinho Fernandes Oct 17 '13 at 08:53
  • @Martinho: This templated operator() is what I needed. Thanks for that! I've re-created a simplified version of your solution in http://coliru.stacked-crooked.com/a/835af28ac3d18e72 . I'm in C++0x, so can't use &&. Instead I pass everything by value. Looking at your solution though, I can't understand what the need for `std::decay` is. I can't find anything in *Josuttis*'s book. By looking at the cppreference.com, I understand that if the template argument is a function, it removes `&` and adds `*` to it. What's the purpose of that? – Grim Fandango Oct 17 '13 at 08:57
  • It also removes `&` if it is something like `foo&`. Basically `decay` simulates pass-by-value semantics, and I used it because I want `And` to always store the function objects by value and not by reference. – R. Martinho Fernandes Oct 17 '13 at 08:59