Mark
Mark

Reputation: 629

Boost bind to operator[]

I'm trying to sort a two-dimensional array vector<vector<int> > a(M,vector<int>(N)) row-wise with respect to its n-th column like this:

sort(a.begin(),a.end(),
  (bind(&vector<int>::operator[],_1,n) >
   bind(&vector<int>::operator[],_2,n)));

however my compiler tells me

error: no matching function for call to ‘bind(<unresolved overloaded function type>, const boost::lambda::placeholder1_type&, int)’
error: no matching function for call to ‘bind(<unresolved overloaded function type>, const boost::lambda::placeholder2_type&, int)’

how should I resolve the call?

PS.: tried an even simpler version of the preceding access to operator[]

  vector<int> a(10);
  (bind(&vector<int>::operator[],_1,_2))(a,2);

which is an adapted copy-cut-and-paste directly from Karlsson's book. Getting

error: no matching function for call to ‘bind(<unresolved overloaded function type>, const boost::lambda::placeholder1_type&, const boost::lambda::placeholder2_type&)’

also for that...

Upvotes: 2

Views: 1428

Answers (4)

Jonathan Mee
Jonathan Mee

Reputation: 38919

I'd like to add a C++11 solution in here, since you don't really gain anything by using Boost here.

A simple lambda solution would work best: std::sort( a.begin(), a.end(), [n]( std::vector< int > first, std::vector< int > second ){ return first[n] < second[n]; } );

Using bind largely increases the complexity of the problem: std::sort( a.begin(), a.end(), std::bind( std::less< int >(), std::bind( static_cast< const int&( std::vector< int >::* )( size_t ) const >( &std::vector< int >::operator[] ), std::placeholders::_1, n ), std::bind( static_cast< const int&( std::vector< int >::* )( size_t ) const >( &std::vector< int >::operator[] ), std::placeholders::_1, n ) );

I don't see any reason to be, but if you're really attached to bind over lambdas you can minimize the nastiness of the static_cast by doing this:

auto columnChooser = std::bind( static_cast< const int&( std::vector< int >::* )( size_t ) const >( &std::vector< int >::operator[] ), std::placeholders::_1, n );
std::sort( a.begin(), a.end(), std::bind( std::less< int >(), columnChooser, columnChooser );

Upvotes: 0

sellibitze
sellibitze

Reputation: 28097

As @soon said, &std::vector<int>::operator[] refers to an overload set. But you can't pass such a thing to a function template and expect it to deduce its type because its type depends on which overload you meant. So, somewhere you would have to disambiguate it manually.

If you can make use of C++11 features, you should be writing

std::sort(a.begin(),a.end(),
    [=](vector<int> const& a, vector<int> const& b) {
        return a[n] > b[n];
    } );

to get rid of the overloading issue. Here, the const overload would be used simply because a and b refer to const vectors.

If you want it to be C++98 compatible another alternative is to write your own functor for applying the subscript operator:

template<class ResultType>
struct apply_subscript {
    typedef ResultType result_type;
    template<class T, class U>
    ResultType operator()(T const& x, U const& y) const { return x[y]; }
    template<class T, class U>
    ResultType operator()(T      & x, U const& y) const { return x[y]; }
};

:::

using namespace boost;
sort(mat.begin(),mat.end(),
    bind(apply_subscript<int>(),_1,n) >
    bind(apply_subscript<int>(),_2,n)
);

HTH

Upvotes: 2

user2328709
user2328709

Reputation: 31

With Boost.Phoenix you can use what @sellibitze mentions in the comments:

#include <iostream>
#include <vector>

#include <boost/phoenix.hpp>

namespace phx=boost::phoenix;

int main()
{
    std::vector<std::vector<int>> matrix
    {
        {1, 2, 3, 4},
        {4, 3, 4, 1},
        {9, 1, 0, 2},
        {3, 1, 5, 1}
    };

    const auto N = 2;

    using phx::arg_names::_1;
    using phx::arg_names::_2;

    std::sort( matrix.begin(), matrix.end(), _1[N] > _2[N] );

    for(const auto& row: matrix)
    {
        for(const auto& elem: row)
            std::cout << elem << ' ';

        std::cout << std::endl;
    }

    return 0;
}

Upvotes: 3

awesoon
awesoon

Reputation: 33671

std::vector has const and non-const versions of operator[], and compiler can't deduce, which overloading should be used. You could do something like this:

template <class R, class T, class... Args>
auto const_mem_fn(R (T::* pm)(Args...) const)
    -> decltype(std::mem_fn(pm))
{
    return std::mem_fn(pm);
}

int main()
{
    std::vector<std::vector<int>> matrix
    {
        {1, 2, 3, 4},
        {4, 3, 4, 1},
        {9, 1, 0, 2},
        {3, 1, 5, 1}
    };

    const auto N = 2;

    std::sort
    (
        matrix.begin(), matrix.end(),
        boost::bind(const_mem_fn(&std::vector<int>::operator[]), _1, N) >
        boost::bind(const_mem_fn(&std::vector<int>::operator[]), _2, N)
    );

    for(const auto& row: matrix)
    {
        for(const auto& elem: row)
            std::cout << elem << ' ';

        std::cout << std::endl;
    }

    return 0;
}

Or, without const_mem_fn:

const int& (std::vector<int>::*op_sq_br)(std::size_t) const = &std::vector<int>::operator[];

std::sort
(
    matrix.begin(), matrix.end(),
    boost::bind(op_sq_br, _1, N) >
    boost::bind(op_sq_br, _2, N)
);

Output:

3 1 5 1
4 3 4 1
1 2 3 4
9 1 0 2

Upvotes: 2

Related Questions