简体   繁体   中英

Using std::tr1::bind with std::vector::push_back

Why my VS2010 can't compile this code:

#include <functional>
#include <vector>
int main()
{
    std::vector<int> vec;
    std::bind(&std::vector<int>::push_back, std::ref(vec), 1)();
    return 0;
}

You should be more specific why this doesn't seem to work for you.

#include <iostream>
#include <tr1/functional>
#include <vector>

int main(int argc, char* argv[]) {
    std::vector<int> vec;
    std::tr1::bind(&std::vector<int>::push_back, std::tr1::ref(vec), 1)();
    std::cout << "vec.size = " << vec.size() << std::endl;
    std::cout << "vec[0] = " << vec[0] << std::endl;
    return 0;
}

$ gcc -o test -lstdc++ test.cpp && ./test
vec.size = 1
vec[0] = 1

Update: Luc Danton is right, the issue here is the overloaded push_back . See question Are there boost::bind issues with VS2010 ? . Also, note that the issue is not limited to push_back , see Visual Studio 2010 and boost::bind .

The bottom line is that what you're trying to do isn't possible in portable C++. std::vector<>::push_back is guaranteed to be overloaded in C++11 compilers, as at a minimum there must be an overload for lvalues and an overload for rvalues.

Usually , when taking the address of an overloaded member function, §13.4/1 in the C++11 FDIS tells us that we can control which overload we're taking the address of thusly:

A use of an overloaded function name without arguments is resolved in certain contexts to a function, a pointer to function or a pointer to member function for a specific function from the overload set. A function template name is considered to name a set of overloaded functions in such contexts. The function selected is the one whose type is identical to the function type of the target type required in the context. [ Note: That is, the class of which the function is a member is ignored when matching a pointer-to-member-function type. —end note ] The target can be

  • an object or reference being initialized,
  • the left side of an assignment,
  • a parameter of a function,
  • a parameter of a user-defined operator,
  • the return value of a function, operator function, or conversion,
  • an explicit type conversion , or
  • a non-type template-parameter.

The overloaded function name can be preceded by the & operator. An overloaded function name shall not be used without arguments in contexts other than those listed. [ Note: Any redundant set of parentheses surrounding the overloaded function name is ignored. —end note ]

The problem comes from §17.6.5.5/2:

An implementation may declare additional non-virtual member function signatures within a class by adding arguments with default values to a member function signature; hence, the address of a member function of a class in the C++ standard library has an unspecified type.

Consequently, it is not portable to ever take the address of a standard library class member function, as the type of such an expression is by definition unknowable except on an implementation-by-implementation basis.

Luc Danton's proposed workaround (specifically, using a lambda) is also what I would recommend:

std::vector<int> vec;
[&](){ vec.push_back(1); }();

Try this:

struct push_back {
    void
    operator()(std::vector<int>& vector, int i) const
    {
        vector.push_back(i);
    }
};

// somewhere else:
std::vector<int> vec;
std::tr1::bind(push_back(), std::tr1::ref(vec), 1)();

With C++03 note that push_back cannot be a local type; with C++11 it can but it would be more idiomatic (and completely equivalent) to use a lambda.

In all likeliness your implementation provides overloads for std::vector<T>::push_back and thus its address would have to be disambiguated. If this is what happened, your compiler should have provided you with an appropriate error message. In all cases, you should explain what you mean by "it's not possible".


The point is not to use such helper functions. – magenta

Then why didn't you put it in the question? I can't read your mind.

You can also try this:

std::vector<int> vec;
void (std::vector<int>::*push_back)(int const&) = &std::vector<int>::push_back;
std::tr1::bind(push_back(), std::tr1::ref(vec), 1)();

Which I believe is not guaranteed to success.

It should propably lok like this:

std::vector<int> vec;
std::tr1::bind(&std::vector<int>::push_back, std::tr1::ref(vec), _1)(1);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM