Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

boost::transform_iterator doesn't work with std::bind( &Pair::first, _1 )?

Tags:

c++

c++11

boost

Traditional task of iterating trough key set of std::map led me to another mess which seems not to be disscused here yet.

In short, this code does not compile (C++11 is heavily used):

typedef std::pair<int, int> Pair;
vector<Pair> v {Pair(1,2), Pair(2,3)};
using namespace std::placeholders;
auto choose_first = std::bind(&Pair::first, _1);
boost::make_transform_iterator(v.begin(), choose_first);

Error message is as follows.

no type named 'result_type' in 'struct std::_Bind<std::_Mem_fn<int std::pair<int, int>::*>(std::_Placeholder<1>)>' 

At the same time, changing std::bind to boost::bind fixes the problem. But there is a code convention in my project that we use std::bind only.

Any suggestions what to do with this? (Should I write bugreport to the Boost team?)

like image 566
Artem Pelenitsyn Avatar asked Mar 22 '12 13:03

Artem Pelenitsyn


2 Answers

There are better ways to iterate over the keys of a std::map (or any container whose value_type is pair<T,U>), namely Boost.Range's map_keys adaptor (there's also a map_values one):

#include <boost/range/adaptor/map.hpp>
#include <utility>
#include <vector>
#include <iostream>

int main(){
  typedef std::pair<int, int> Pair;
  std::vector<Pair> v {Pair(1,2), Pair(2,3)};
  for(auto& first : v | boost::adaptors::map_keys){
    std::cout << first << " ";
  }
}

But back to your problem: All Boost libraries use the Boost.Utility function result_of, which won't fall back to std::result_of for whatever reason, and also won't use decltype if it's available without you telling it to. You do so by putting #define BOOST_RESULT_OF_USE_DECLTYPE before the first Boost include.

That, however, still didn't make your code compile with Clang 3.1 SVN + libc++. Here's the code I used:

#define BOOST_RESULT_OF_USE_DECLTYPE
#include <boost/iterator/transform_iterator.hpp>
#include <utility>
#include <vector>
#include <functional>

int main(){
  typedef std::pair<int, int> Pair;
  std::vector<Pair> v {Pair(1,2), Pair(2,3)};
  using namespace std::placeholders;
  auto choose_first = std::bind(&Pair::first, _1);
  boost::make_transform_iterator(v.begin(), choose_first);
}

Compiled with:

clang++ -std=c++0x -stdlib=libc++ -Wall -pedantic -Ipath/to/boost -Wno-mismatched-tags t.cpp

GCC 4.7 seems to accept this just fine, so I guess it's a bug in libc++.

like image 96
Xeo Avatar answered Sep 29 '22 23:09

Xeo


It seems you need to define BOOST_RESULT_OF_USE_DECLTYPE before including the boost library to actually use the C++11 method to get the result type, instead of relying on the legacy result_type member. This compiles fine on g++ 4.6 and 4.8:

#define BOOST_RESULT_OF_USE_DECLTYPE
// ^

#include <vector>
#include <boost/iterator/transform_iterator.hpp>
#include <functional>

int main()
{

typedef std::pair<int, int> Pair;
std::vector<Pair> v {Pair(1,2), Pair(2,3)};
using namespace std::placeholders;
auto choose_first = std::bind(&Pair::first, _1);
boost::make_transform_iterator(v.begin(), choose_first);

}

See the discussion thread http://lists.boost.org/boost-users/2012/01/72856.php to know why it is not enabled by default.


You could also workaround by using std::mem_fn instead of std::bind,

auto choose_first = std::mem_fn(&Pair::first);

which does define those legacy type members (§20.8.10/2). Since std::mem_fn is still part of the standard library I believe there's no problem using it in your team… ?


As a last resort, you could always use the C++03 way to declare a function object (of course we knew std::unary_function is deprecated):

template <typename T>
struct FirstChooser :
        public std::unary_function<const T&, typename T::first_type>
{
    typename T::first_type operator()(const T& input) const
    {
        return input.first;
    }
};

...

FirstChooser<Pair> choose_first;
like image 29
kennytm Avatar answered Sep 29 '22 22:09

kennytm