0

When using std::bind, I was able to bind data members in VS2010 by passing a pointer or an iterator rather than the object itself. However, it no longer seems to work in VS2012:

#include <vector>
#include <utility>
#include <iostream>
#include <functional>

using namespace std;

int main()
{
    vector<pair<string, int>> v; 
    v.push_back(make_pair("abc", 10));
    auto f = bind(&pair<string, int>::second, v.begin());
    int res = f();
    cout << res << endl;
    return 0;
}

(http://ideone.com/n1KWu)

GCC also compiles & runs this code fine, but VS2012 gives me an error:

error C2440: 'initializing' : cannot convert from 'std::_Do_call_ret<_Forced,_Ret,_Funx,_Btuple,_Ftuple>::type' to 'int'
1>          with
1>          [
1>              _Forced=false,
1>              _Ret=void,
1>              _Funx=std::_Pmd_wrap<int std::pair<std::string,int>::* ,int,std::pair<std::string,int>>,
1>              _Btuple=std::tuple<std::_Vector_iterator<std::_Vector_val<std::_Simple_types<std::pair<std::string,int>>>>>,
1>              _Ftuple=std::tuple<>
1>          ]
1>          Expressions of type void cannot be converted to other types

Note that if I pass an std::pair instance instead of an iterator or a pointer to it, then VS2012 is happy.

What is the problem here?

Alex Korban
  • 14,916
  • 5
  • 44
  • 55

2 Answers2

1

std::mem_fn (and std::bind) is specified and implemented in terms of the INVOKE facility, see §20.8.10 [func.memfn] p1

Returns: A simple call wrapper (20.8.1) fn such that the expression fn(t, a2, ..., aN) is equivalent to INVOKE(pm, t, a2, ..., aN) (20.8.2).

As such, it's very likely a bug in MSVC's implementation of INVOKE, specifically the fourth requirement:

§20.8.2 [func.require] p1

Define INVOKE(f, t1, t2, ..., tN) as follows:

  • (t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;
  • ((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous item;
  • t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;
  • (*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 is not one of the types described in the previous item;
  • f(t1, t2, ..., tN) in all other cases.

The bold part basically says that when f is a member pointer (of any kind) and t1 is not a reference, try to dereference it and apply the member pointer after that. This should be the case for your code, as an iterator is not a reference to T. GCC implements this correctly.

I advise filing a bug report on MS Connect.

Community
  • 1
  • 1
Xeo
  • 129,499
  • 52
  • 291
  • 397
0

If you check a reference like this you will see that the first arguments should be

function object that will be bound to some arguments

In other words, you should not be able to bind data members or variables, only functions and function-like objects.

To quote from the C++11 standard (section 20.8.9):

The function template bind returns an object that binds a callable object passed as an argument to additional arguments.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • You can perfectly fine bind a data member. `std::_Pmd_wrap`, the "Pmd" is "Pointer to Member Data". See also [this answer of mine](http://stackoverflow.com/a/12638715/500104). The problem here is the iterator binding. An iterator doesn't support `(it->*member_ptr)`, while a normal pointer does and a reference supports `(r.*member_ptr)`. It's all defined in terms of the `INVOKE` facility that describes how the whole binding machinery should work. – Xeo Oct 19 '12 at 13:16
  • My understanding is that data members are included in the definition of a callable object, so it's legal to use them with bind (I've been doing that for years with boost::bind by the way). Also, as I noted in the question, the problem isn't limited to iterators. I can't use a regular pointer either, so I don't think @Xeo's comment clarifies the situation for me. And why does my code work fine with GCC? – Alex Korban Oct 20 '12 at 21:22
  • 1
    @Alex: It most likely works for GCC because *their* iterators have `operator->*`. And it most likely does *not* work for MSVC thanks to a bug in MSVC's implementation (I encountered that one before). – Xeo Oct 20 '12 at 21:45
  • 1
    @Xeo, if it's a bug in MSVC then I guess it's the answer to my question. Would you like to turn your comment into an answer so I can accept it? – Alex Korban Oct 22 '12 at 22:51