Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No matching call error occuring for boost::ref but not with std::ref

I've written some code which counts the number of elements of vector using a functor and the ref and bind templates from boost:: or std:: (for C++11) namespaces. I'm using a #define to switch between boost:: and std:: namespaces. I'm using boost version 1.53 and my compilation command is g++ test.cpp -std=c++11. I've tried with gcc versions 4.7.2 and 4.6.3 and I get the same errors with both.

I have 3 questions:

  1. I don't understand the error that is generated for Example 2.
  2. Is it possible to make code like this portable just by switching namespaces?
  3. Is there a good reference describing in detail the differences between the std and boost versions of bind, ref and function? (I saw this question but the answers don't mention ref or function)

Thanks!

P.S. The example just illustrates my problem, I know about size() for std::vector :-)

//#define USE_STD

#ifdef USE_STD
#include <functional>
using namespace std::placeholders;
namespace impl = std;
#else
#include <boost/version.hpp>
#include <boost/bind.hpp>
#include <boost/ref.hpp>
namespace impl = boost;
#endif

#include <iostream>
#include <algorithm>
#include <vector>

class Item {
    int id_;

public:
    Item(int id) : id_(id) {};
};

template <typename ITEM>
class Counter {
    int count_;

public:
    // typedef void result_type; // adding this fixes Example 3 when impl=boost
    Counter() : count_(0) {};
    void operator()(ITEM* item) {count_++;}
    int operator()() {return count_;}
};

//------------------------------------------------------------------------------
int main(int argc, char *argv[])
{
#ifndef USE_STD
    std::cout << "BOOST_LIB_VERSION=" << BOOST_LIB_VERSION << std::endl;
#endif

    // allocate
    typedef std::vector<Item*> ItemVec;
    ItemVec vec;
    for (int i = 0; i < 9; ++i) {vec.push_back(new Item(i));}

    // Example 1, works for BOTH
    Counter<Item> f1;
    f1 = std::for_each(vec.begin(), vec.end(), f1);
    std::cout << "f1()=" << f1() << std::endl;

    // Example 2, works with impl=std ONLY
    // COMPILE ERROR with impl=boost: "no match for call to ‘(boost::reference_wrapper<Counter<Item> >) (Item*&)’"
    Counter<Item> f2;
    std::for_each(vec.begin(), vec.end(), impl::ref(f2));
    std::cout << "f2()=" <<  f2() << std::endl;

    // Example 3, works with impl=std ONLY
    // COMPILE ERROR with impl=boost "no type named ‘result_type’ in ‘class Counter<Item>’"
    // this can fixed by adding the typedef described above
    Counter<Item> f3;
    std::for_each(vec.begin(), vec.end(), impl::bind(impl::ref(f3), _1));
    std::cout << "f3()=" << f3() << std::endl;

    // clean up
    for (ItemVec::iterator it = vec.begin(); it != vec.end(); ++it) {
        delete *it;
    }
    vec.clear();

    return 0;
}
like image 830
mkm Avatar asked Feb 25 '13 18:02

mkm


2 Answers

Example 2 fails because boost::reference_wrapper doesn't have a member operator() which forwards the argument(s), unlike std::reference_wrapper. As such, it's only useful for passing normal arguments by reference, not functions or functors which are expected to be called.

Example 3 fails because Boost.Bind relies on a specific protocol to get the result type of the function or functor you pass, if you use the version without explicit return type. If you pass it a pointer-to-function or pointer-to-member-function, the returnd binder object has a nested result_type set to the return type of said PTF or PTMF. If you pass a functor, it needs a nested result_type.
std::bind, on the other hand, simply has no nested result_type if your functor doesn't have one.

Note that you can, as I said, explicitly provide the result type to both boost::bind and std::bind:

std::for_each(vec.begin(), vec.end(), impl::bind<void>(impl::ref(f3), _1));
//                                              ^^^^^^

Which fixes the example and makes it compile.

like image 130
Xeo Avatar answered Oct 30 '22 19:10

Xeo


std::ref has 1 major advantage over boost::ref: It provides a varadic perfect-forwarding operator(), which will forward the call onto its contained reference.

boost::ref can't practically do this, as it would require a significant number of overloads. To allow this, however, boost::bind (and a few other classes) all provide special handling for boost::reference_wrapper.

like image 35
Dave S Avatar answered Oct 30 '22 20:10

Dave S