Шаблонный функтор C ++ в лямбда-выражении

Эта первая часть была решена комментариями Эрика ниже, но привела к вторичной проблеме, которую я описываю после горизонтального правила. Спасибо, Эрик!

Я пытаюсь передать функтор, который является шаблонным классом, методу create_thread класса boost thread_group вместе с двумя параметрами для функтора. Однако я не могу выйти за рамки моей текущей ошибки компиляции. С приведенным ниже кодом:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/thread.hpp>
#include <vector>

using namespace boost::lambda;
using namespace std;

namespace bl = boost::lambda;

template<typename ftor, typename data>
class Foo
{
public:
    explicit Foo()
    {
    }
    void doFtor ()
    {
        _threads.create_thread(bind(&Foo<ftor, data>::_ftor, _list.begin(), _list.end()));
        //_threads.create_thread(bind(_ftor, _list.begin(), _list.end()));
        _threads.join_all();
    }

private:
    boost::thread_group _threads;
    ftor _ftor;
    vector<data> _list;
};

template<typename data>
class Ftor
{
public:
    //template <class Args> struct sig { typedef void type; }

    explicit Ftor () {}

    void operator() (typename vector<data>::iterator &startItr, typename vector<data>::iterator &endItr)
    {
        for_each(startItr, endItr, cout << bl::_1 << constant("."));
    }
}

Я также попробовал typedef-ing 'type', поскольку думал, что моя проблема может иметь какое-то отношение к шаблону Sig, поскольку сам функтор является шаблоном.

Я получаю следующую ошибку:

error: no matching function for call to ‘boost::lambda::function_adaptor<Ftor<int> Foo<Ftor<int>, int>::*>::apply(Ftor<int> Foo<Ftor<int>, int>::* const&, const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int>> >&, const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >&)’

с кучей преамбулы заранее.

Заранее благодарю за любую помощь!


Хорошо, я изменил код, взяв предложения Эрика ниже, в результате получился следующий код:

#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/thread.hpp>
#include <vector>

using namespace boost::lambda;
using namespace std;

namespace bl = boost::lambda;

template<typename ftor, typename data>
class Foo
{
public:
    explicit Foo()
    {
    }
    void doFtor ()
    {
        _threads.create_thread(bl::bind(boost::ref(_ftor), _list.begin(), _list.end()));
        _threads.join_all();
    }

private:
    boost::thread_group _threads;
    ftor _ftor;
    vector<data> _list;
};

template<typename data>
class Ftor
{
public:
    typedef void result_type;

    explicit Ftor () {}

    result_type operator() (typename vector<data>::iterator &startItr, typename vector<data>::iterator &endItr)
    {
        for_each(startItr, endItr, cout << bl::_1 << constant("."));
        return ;
    }
};

Однако это приводит к другой ошибке компиляции:

/usr/local/include/boost/lambda/detail/function_adaptors.hpp:45: error: no match for call to ‘(Ftor<int>) (const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >&, const __gnu_cxx::__normal_iterator<int*, std::vector<int, std::allocator<int> > >&)’
ftor.h:41: note: candidates are: void Ftor<data>::operator()(typename std::vector<data, std::allocator<_CharT> >::iterator&, typename std::vector<data, std::allocator<_CharT> >::iterator&) [with data = int]
/usr/local/include/boost/lambda/detail/function_adaptors.hpp:45: error: return-statement with a value, in function returning 'void'

Похоже, что определив void как result_type, теперь ожидается, что operator () что-то вернет. Я попытался вернуть result_type из функции, но это также вызвало ошибки. Любые идеи?


person Demps    schedule 26.02.2010    source источник
comment
Какой компилятор вы используете и какую версию boost? Здесь компилируется нормально, за исключением того, что отсутствует заголовок iostream (GCC 4.3.3, boost 1.42)   -  person coelhudo    schedule 26.02.2010
comment
Интересно. Я пробовал это на своем Mac, не уверен в версии gcc, но он использует boost 1.42. И я воспроизвел на своем Linux-боксе, который использует немного более старые версии: gcc 4.1.1 и boost 1.37   -  person Demps    schedule 26.02.2010
comment
Показанный код компилируется, но создание экземпляра Foo::doFtor() вызывает ошибки компиляции. Например, попробуйте добавить int main(){Foo<Ftor<int>, int> f; f.doFtor();}   -  person Éric Malenfant    schedule 26.02.2010
comment
Ах да, я должен был также предоставить свой основной. Компилятор не будет генерировать код для метода doFtor, пока он не будет создан / вызван.   -  person Demps    schedule 26.02.2010
comment
О вашем редактировании: проблема с оператором (), принимающим свои аргументы как неконстантные ссылки, остается. Вот что говорится в первой цитируемой вами ошибке: компилятор ищет оператор (), принимающий ссылки на константы. Как я писал в своем ответе, вам все равно нужно изменить подпись operator (), чтобы она принимала итераторы копированием или const &   -  person Éric Malenfant    schedule 27.02.2010
comment
Для ясности, вот код, который мне подходит: codepad.org/j4mibHWS   -  person Éric Malenfant    schedule 27.02.2010
comment
Понял, Эрик, спасибо за мельницу. Теперь он компилируется :)   -  person Demps    schedule 27.02.2010


Ответы (1)


Sig (или в вашем случае просто typedef void result_type;.

IIRC, lambda :: bind создает константные копии своих аргументов.

Таким образом, возникает проблема с функторами с неконстантным оператором (). Это решается путем создания Ftor :: operator () const или путем обертывания (в doFtor ()), _ftor с помощью boost :: ref

Похожая проблема и с итераторами. Перенос в boost :: ref здесь не будет работать напрямую, потому что в конечном итоге он будет использовать ссылку на временный. Более простое решение - изменить Ftor :: operator () так, чтобы его аргументы принимались копией.

Таким образом, проще всего изменить Ftor так, чтобы его operator () был const и он принимал свои аргументы путем копирования:

void operator() (typename vector<data>::iterator startItr, typename vector<data>::iterator endItr)const

Если вы действительно не можете сделать Ftor :: operator () const, вы можете изменить doFtor () следующим образом (но все же необходимо заставить Ftor :: operator () принимать свои аргументы по копии):

_threads.create_thread(bind(boost::ref(_ftor), _list.begin(), _list.end()));
person Éric Malenfant    schedule 26.02.2010
comment
С изменениями, упомянутыми в варианте 2, я получаю следующее: - ошибка: при передаче аргумента 1 'const boost :: reference_wrapper ‹T› boost :: ref (T &) [с T = __gnu_cxx :: __ normal_iterator ‹int *, std: : vector ‹int, std :: allocator ‹int›› ›] ' - person Demps; 26.02.2010
comment
В самом деле, использование boost :: ref в _list.begin () - не лучшая идея, так как это приводит к передаче ссылки на временный объект. Я обновлю ответ - person Éric Malenfant; 26.02.2010
comment
@Eric: Я изменил код, как вы предложили, но это также вызывает ошибку компиляции. На этот раз с возвращаемым типом, который, как мне кажется, зависит от typedef. Я изменил приведенный выше вопрос, чтобы отразить изменения. - person Demps; 26.02.2010