Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create pointer to member for map value type in vc2010

Tags:

c++

visual-c++

We have a problem porting our code to the slightly less old version 2010 of VC++.

The issues is caused by the implementation of map in VC which results in a derived to base conversion of a pointer-to-member in a non-type-argument being required:

#include <map>
#include <algorithm>

template <typename MapValueType, int MapValueType::*var>
bool f (MapValueType const & v);

int main ()
{
  typedef std :: map<int, int> MapType;
  MapType m;
  std :: find_if (m.begin ()
      , m.end ()
      , f<MapType::value_type, & MapType::value_type::second> );
}

The following message is generated:

Standard conversion from pointer-to-member of base to pointer-to-member of derived is not applied for template arguments file.cc(x) : error C2973: 'f' : invalid template argument 'int std::_Pair_base<_Ty1,_Ty2>::* '

So it seems that the implementation of value_type in std::map has the pair in a base class.

Any ideas on how to solve this and keep the pointer-to-member as a non-type-argument?

Is our only option to change the structure so that f is a functor with a member pointer-to-member?

like image 802
Richard Corden Avatar asked Jan 21 '13 13:01

Richard Corden


2 Answers

Why do you insist on keeping the pointer-to-member as non-type-template parameter/argument?

Anyway, I think that you could use this, if you can be limited to either Visual Studio 2010 or compilers with decltype()

template <typename Class, typename Type>
Class
get_class_type (Type Class:: *);
//...
it = std::find_if(m.begin(), m.end(),
    f<decltype(get_class_type(&MapType::value_type::second)), &MapType::value_type::second>);
like image 157
wilx Avatar answered Oct 23 '22 10:10

wilx


Provided that your code should compile IMO (and it does on GCC 4.7.2 and Clang 3.2), I believe your design is unnecessarily intricate. A pair only has two member variables, so you are going to access either the first or the second.

I do not see the need for a functor object either: just use a boolean template argument to determine whether the code shall work on the first or on the second member variable. Here's a possibility:

#include <map>
#include <algorithm>

template <typename MapValueType, bool first>
bool f (MapValueType const & p)
{
    auto& v = (first) ? p.first : p.second;
    // ... do your work on v ...    
}

int main ()
{
    typedef std :: map<int, int> MapType;
    MapType m;

    // Will work on the `first` member
    std::find_if(m.begin (), m.end (), f<MapType::value_type, true>);

    // Will work on the `second` member
    std::find_if(m.begin (), m.end (), f<MapType::value_type, false>);
}

If you really cannot change your client code nor your code inside the f() function, then you could go for this VS2010-specific hack:

// Add "_MyBase" here... works, but ugly IMO
template <typename MapValueType, int MapValueType::_Mybase::* var>
bool f(MapValueType const & v);

// And the client side could stay unchanged...
int main ()
{
    typedef std :: map<int, int> MapType;
    MapType m;
    std::find_if(
        m.begin(), 
        m.end (), 
        f<MapType::value_type, &MapType::value_type::second>
        );
}

Finally, if your code has to compile on other platforms and all the constraints on the non-modifiability of the function's and client code still hold, then you can define a preprocessor macro that expands to _Mybase:: for VS2010 and to the empty string for other compilers.

like image 31
Andy Prowl Avatar answered Oct 23 '22 12:10

Andy Prowl