проблема с лямбда-привязкой?

Я новый новичок с Boost. И вот мой тестовый код,

  using namespace boost::lambda;
  std::vector<std::string> strings; 
  strings.push_back("Boost"); 
  strings.push_back("C++"); 
  strings.push_back("Libraries"); 

  std::vector<int> sizes; 

  std::for_each(
   strings.begin(),
   strings.end(),
   bind(
  &std::vector<int>::push_back,
  sizes,
  bind<std::size_t>(&std::string::size, _1)));

  std::for_each(sizes.begin(), sizes.end(), var(std::cout)<<_1);

построить проект и получить ошибку:

ошибка C2665: 'boost::lambda::function_adaptor::apply': ни одна из двух перегрузок не смогла преобразовать все типы аргументов

Интересно, что случилось? Очень ценю.


person leo    schedule 29.10.2010    source источник
comment
Что вы пытаетесь сделать со строками в векторе?   -  person stefanB    schedule 29.10.2010
comment
bind и lambda имеют заполнители _1, bind в анонимном пространстве имен, lambda в пространстве имен lambda. попробуйте не использовать все пространство имен boost:: lambda   -  person Anycorn    schedule 29.10.2010


Ответы (2)


Первая проблема заключается в том, что std::vector::push_back является перегруженной функцией в C++0x (существует перегрузка со ссылочным параметром lvalue и перегрузка с ссылочным параметром rvalue).

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

Вам нужно будет привести функцию-указатель к правильному типу, иначе это не будет работать с реализацией библиотеки C++0x (функция-указатель на-член будет неоднозначной). Вам понадобиться:

(void (std::vector<int>::*)(const int&))&std::vector<int>::push_back

Используя функциональную библиотеку С++ 0x, работает следующее (это должно работать и с Boost, если вы замените std:: на boost::; я просто не могу это проверить):

std::for_each(
    strings.begin(),
    strings.end(),
    std::bind(
       (void (std::vector<int>::*)(const int&))&std::vector<int>::push_back,
       std::ref(sizes),
       std::bind(&std::string::size, std::placeholders::_1)));

Обратите внимание, что это настоящий беспорядок, и гораздо понятнее просто использовать цикл for:

for(std::vector<int>::const_iterator i(strings.begin()); i != strings.end(); ++i)
{
    sizes.push_back(i->size());
}

Или, если у вас есть компилятор, поддерживающий лямбда-выражения:

std::for_each(strings.begin(), strings.end(), 
              [&](const std::string& s) { sizes.push_back(s.size()); });

Или, для большего удовольствия:

std::transform(strings.begin(), strings.end(), std::back_inserter(sizes),
               [](const std::string& s) { return s.size(); });
person James McNellis    schedule 29.10.2010
comment
Спасибо, приложение работает, но меня немного смущает этот код: (void (std::vector‹int›::*)(const int&))&std::vector‹int›::push_back. Мне нужно узнать больше об этом. - person leo; 31.10.2010
comment
@user: &std::vector<int>::push_back принимает адрес именованной функции-члена. Часть, которая ему предшествует, является приведением, как если бы вы сказали (int)3.0, которое приводит двойное значение 3.0 к типу int. В этом случае мы приводим указатель типа к функции, которая является членом std::vector<int> и которая принимает один аргумент типа const int& и возвращает void. - person James McNellis; 31.10.2010
comment
Я попробовал лямбда-выражения std::for_each(strings.begin(), strings.end(), [&](const std::string& s) {sized.push_back(s.size()); }); и std::transform(...), но выдала синтаксическую ошибку: [ ], поэтому я пропустил какой-то заголовочный файл или по другим причинам? - person leo; 31.10.2010
comment
@leo: ваш компилятор может не поддерживать лямбда-выражения. Последние версии Visual C++, Intel C++ и g++ поддерживают их. - person James McNellis; 31.10.2010
comment
Мне жаль, что я запускал его как на VS2005, так и на Ubuntu 10.04 с g++ 4.4.3, и оба выдавали эту ошибку. Должен ли я указывать пространство имен или компилировать код с некоторыми библиотеками? - person leo; 01.11.2010
comment
@leo: Visual C++ 2010 — первая версия, поддерживающая лямбда-выражения (в 2005 году они были не более чем предложенной функцией языка). Если я правильно помню, g++ 4.5 — первая версия, которая полностью поддерживает лямбда-выражения (тут я могу ошибаться, я не использую g++). Intel C++ 11 была первой версией этого компилятора, поддерживающей лямбда-выражения. - person James McNellis; 01.11.2010
comment
может быть, это было причиной, я нашел, что у кого-то была такая же ошибка на форуме, и еще раз спасибо. - person leo; 01.11.2010

Или вы можете создать свой собственный объект функции:

template <typename T>
struct c_inserter
{
    T& c;

    c_inserter(T& c) : c(c) {}
    void operator()(string& v) { c.push_back(v.size()); }
};

Затем используйте его (обратите внимание, что ostream_iterator и copy заменяют другой lambda):

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>

#include <boost/lambda/lambda.hpp>

int main() 
{
    std::vector<std::string> strings;
    strings.push_back("Boost");
    strings.push_back("C++");
    strings.push_back("Libraries");

    std::vector<int> sizes;

    std::for_each(
            strings.begin(),
            strings.end(),
            c_inserter< vector<int> >(sizes));

    copy(sizes.begin(), sizes.end(), ostream_iterator<int>(cout,":"));
    cout << endl;
}
person stefanB    schedule 29.10.2010