Использование std::tr1::bind с std::vector::push_back

Почему мой VS2010 не может скомпилировать этот код:

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

person magenta    schedule 15.05.2011    source источник
comment
Вставьте полученную ошибку. Даже если мы готовы помочь, мы не экстрасенсы.   -  person ereOn    schedule 15.05.2011
comment
Я не вижу в этом ошибки. Я запускаю этот код, и он работает хорошо. Обязательно включите tr1/functional.   -  person Łukasz Milewski    schedule 15.05.2011
comment
Он не компилируется в VS2010, без заголовка ‹tr1/functional›, только ‹functional›. Что я делаю неправильно?   -  person magenta    schedule 15.05.2011
comment
VS2010 уже предоставляет функциональность c++0x. Таким образом, чтобы использовать std::bind, просто включите <functional>.   -  person Paul Michalik    schedule 15.05.2011


Ответы (4)


Попробуй это:

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)();

С С++ 03 обратите внимание, что push_back не может быть локальным типом; с С++ 11 это возможно, но было бы более идиоматично (и полностью эквивалентно) использовать лямбда.

По всей вероятности, ваша реализация предоставляет перегрузки для std::vector<T>::push_back, поэтому его адрес должен быть устранен. Если это то, что произошло, ваш компилятор должен был предоставить вам соответствующее сообщение об ошибке. В всех случаях вы должны объяснить, что вы подразумеваете под "это невозможно".


Дело в том, чтобы не использовать такие вспомогательные функции. - пурпурный

Тогда почему вы не указали это в вопросе? Я не могу читать твои мысли.

Вы также можете попробовать это:

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)();

Что, я считаю, не гарантирует успеха.

person Luc Danton    schedule 15.05.2011
comment
@magenta Пожалуйста, измените свой вопрос тогда - person Luc Danton; 15.05.2011

Вы должны быть более конкретными, почему это не работает для вас.

#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

Обновление: Люк Дантон прав, проблема здесь в перегруженном push_back. См. вопрос Есть ли проблемы boost::bind с VS2010?. Также обратите внимание, что проблема не ограничивается push_back, см. Visual Studio 2010 и boost::bind.

person Ronald Blaschke    schedule 15.05.2011
comment
Просто не компилируется. И проблема, похоже, в #include ‹tr1/functional›. Я использую VS2010, и такого заголовка нет. - person magenta; 15.05.2011
comment
@magenta: в VS2010 просто нет заголовков tr1, они встроены в заголовки, отличные от tr1. Кроме того, VS2010 импортирует все важные имена tr1 в обычное пространство имен std, поэтому просто включите <functional> и используйте std::bind без tr1. - person Xeo; 15.05.2011

Суть в том, что то, что вы пытаетесь сделать, невозможно в portable C++. std::vector<>::push_back гарантированно будет перегружен в компиляторах C++11, поскольку как минимум должна быть перегрузка для lvalue и перегрузка для rvalue.

Обычно при получении адреса перегруженной функции-члена §13.4/1 в FDIS C++11 говорит нам, что мы можем контролировать, адрес какой перегрузки мы берем следующим образом:

Использование имени перегруженной функции без аргументов разрешается в определенных контекстах для функции, указателя на функцию или указателя на функцию-член для конкретной функции из набора перегрузки. В таких контекстах считается, что имя шаблона функции именует набор перегруженных функций. Выбрана функция, тип которой идентичен типу функции целевого типа, необходимого в контексте. [ Примечание: То есть класс, к которому относится функция. член игнорируется при сопоставлении типа указателя на функцию-член. —конец примечания ] Цель может быть

  • инициализируемый объект или ссылка,
  • левая часть задания,
  • параметр функции,
  • параметр пользовательского оператора,
  • возвращаемое значение функции, операторной функции или преобразования,
  • явное преобразование типа или
  • параметр-шаблона, не являющийся типом.

Перед именем перегруженной функции может стоять оператор &. Имя перегруженной функции не должно использоваться без аргументов в контекстах, отличных от перечисленных. [ Примечание. Любой лишний набор скобок, окружающих имя перегруженной функции, игнорируется. —конец примечания ]

Проблема возникает из §17.6.5.5/2:

Реализация может объявлять дополнительные невиртуальные сигнатуры функций-членов внутри класса, добавляя аргументы со значениями по умолчанию к сигнатуре функции-члена; поэтому адрес функции-члена класса в стандартной библиотеке C++ имеет неопределенный тип.

Следовательно, невозможно когда-либо взять адрес функции-члена класса стандартной библиотеки, поскольку тип такого выражения по определению неизвестен, кроме как на основе реализации за реализацией.

Предложенный Люком Дантоном обходной путь (в частности, с использованием лямбды) также является тем, что я бы порекомендовал:

std::vector<int> vec;
[&](){ vec.push_back(1); }();
person ildjarn    schedule 15.05.2011
comment
Да, лямбда, вероятно, лучший выбор, если мы используем C++0x. Но на самом деле мне просто было любопытно, почему VS2010 не смог скомпилировать мой код. - person magenta; 15.05.2011

Вероятно, это должно выглядеть так:

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