Domanda

I have the following code (using boost 1.55 on MSVC9):

struct pair_first_impl
{
   template<class TPair> struct result { typedef typename TPair::first_type type; };

   template<class TPair>
   typename TPair::first_type const& operator() (TPair const& pair) const
   {
      return pair.first;
   }

   template<class TPair>
   typename TPair::first_type& operator() (TPair& pair)
   {
      return pair.first;
   }
};

static phx::function<pair_first_impl> pair_first;

int test()
{
   std::map<int, std::string> mymap;
   std::find_if(mymap.begin(), mymap.end(), pair_first(_1) == 1);
}

I'm getting a compiler error regarding pair_first_impl::result::type, which says:

error C2825: 'TPair': must be a class or namespace when followed by '::'
see reference to class template instantiation 'pair_first_impl::result<TPair>' being compiled
        with
        [
            TPair=const pair_first_impl (std::pair<const int,std::string> )
        ]

For some reason, it looks like it is passing in a function type (?) into my TPair template argument instead of the std::pair type directly.

Can anyone help me figure out what I'm doing wrong here?

È stato utile?

Soluzione

I found the solution by reviewing the result_of protocol documentation (which is separate from phoenix; I was expecting phoenix docs to explain):

struct pair_first_impl
{
   template<class> struct result;

   template<class F, class TPair>
   struct result<F(TPair)>
   {
      typedef typename boost::remove_reference<TPair>::type actual_type;
      typedef typename actual_type::first_type type;
   };

   template<class TPair>
   typename TPair::first_type const& operator() (TPair const& pair) const
   {
      return pair.first;
   }

   template<class TPair>
   typename TPair::first_type& operator() (TPair& pair)
   {
      return pair.first;
   }
};

static phx::function<pair_first_impl> pair_first;

int test()
{
   std::map<int, std::string> mymap;
   std::find_if(mymap.begin(), mymap.end(), pair_first(_1) == 1);
   return 0;
}

The problem was that I thought that the type passed into the template parameter for the nested result struct was the type of the first parameter, which it isn't. It's actually the whole function type. So a template specialization of result must be created the can be used to extract the type of the first argument. You can then use this to access first_type in the pair.

My _1 placeholder is valid because at the top of my source file I'm doing the following:

using namespace boost::phoenix::placeholders;
namespace phx = boost::phoenix;

Altri suggerimenti

You're using the wrong placeholder _1. You need one that is actually a phoenix actor:

std::find_if(mymap.begin(), mymap.end(), pair_first(phx::placeholders::_1) == 1);

OTOH, your functor has inconsistent result_type protocol. This might not bite you when you use BOOST_SPIRIT_RESULT_OF_USE_DECLTYPE. Why don't you just use a bind? This will get all the deduction correct for you without the work:

using namespace phx::arg_names;

void test()
{
   std::map<int, std::string> mymap;

   using Pair = std::pair<const int, std::string>;
   std::find_if(mymap.begin(), mymap.end(), phx::bind(&Pair::first, arg1) == 1);
}

Of course, you can detect the pair-type if you want.

Full code Live On Coliru

#include <boost/phoenix.hpp>
#include <algorithm>
#include <map>

namespace phx = boost::phoenix;

struct pair_first_impl
{
   template<class TPair> struct result { typedef typename TPair::first_type const& type; };

   template<class TPair>
   typename TPair::first_type const& operator() (TPair const& pair) const {
      return pair.first;
   }

   template<class TPair>
   typename TPair::first_type& operator() (TPair& pair) {
      return pair.first;
   }
};

static phx::function<pair_first_impl> pair_first;


void test1()
{
   using phx::placeholders::_1;

   std::map<int, std::string> mymap;
   std::find_if(mymap.begin(), mymap.end(), pair_first(_1) == 1);
}

void test2()
{
   using Pair = std::pair<const int, std::string>;
   using namespace phx::arg_names;

   std::map<int, std::string> mymap;
   std::find_if(mymap.begin(), mymap.end(), phx::bind(&Pair::first, arg1) == 1);
}

void test3()
{
   std::map<int, std::string> mymap;
   using Pair = decltype(mymap)::value_type;
   using namespace phx::arg_names;

   std::find_if(mymap.begin(), mymap.end(), phx::bind(&Pair::first, arg1) == 1);
}

int main()
{
    test1();
    test2();
    test3();
}

This doesn't answer your question, but it gives a workaround that is based on existing implementation: (untested code)

#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/phoenix/fusion/at.hpp>
...
int test()
{
   std::map<int, std::string> mymap;
   std::find_if(mymap.begin(), mymap.end(), boost::phoenix::at_c<0>(_1) == 1);
}

(This without even mention that with C++11 Lambdas you don't need any of this)

Even if this works, your question is a valid one. I suggest two experiments, 1) use enable_if to discard TPair if it is a phoenix expression. 2) Make your operator() more specific to std::pair, like this:

   ...
   template<class T1, T2> // change `result` accordingly.
   T1& operator() (std::pair<T1, T2>& pair)
   {
      return pair.first;
   }
   ...
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top