2012-07-15 2 views
8

Я пытаюсь написать функцию, которая возьмет функтор в качестве аргумента, вызовет функтор, а затем вернет возвращаемое значение, завернутое в boost::shared_ptr ,«<class name> не предоставляет ошибку оператора вызова при попытке обернуть функцию возвращаемого значения

Следующие отказываются компилировать, и у меня все идеи. Я получаю «std :: vector < std :: string> не предоставляет оператор вызова» (примерно). Я использую Clang 3.1 на Mac OS X.

template< typename T > 
boost::shared_ptr<T> ReturnValueAsShared(
    boost::function< T() > func) 
{ 
    return boost::make_shared<T>(func()); 
} 

Это контекст, в котором я пытаюсь использовать:

make_shared< packaged_task< boost::shared_ptr< std::vector<std::string> > > >(
    bind(ReturnValueAsShared< std::vector<std::string> >, 
     bind([a function that returns a std::vector<std::string>]))); 

EDIT: Вот полный автономный тест. Этот код не компилировать с той же ошибкой, и для жизни меня я не могу видеть, что это не так:

#include <boost/make_shared.hpp> 
#include <boost/shared_ptr.hpp> 
#include <boost/function.hpp> 
#include <boost/bind.hpp> 

#include <string> 
#include <vector> 

std::vector<std::string> foo(std::string a) 
{ 
    std::vector<std::string> vec; 
    vec.push_back(a); 
    return vec; 
} 

template< typename T > 
boost::shared_ptr<T> ReturnValueAsShared(
    boost::function< T() > func) 
{ 
    return boost::make_shared<T>(func()); 
} 

int main() 
{ 
    auto f = boost::bind(ReturnValueAsShared< std::vector<std::string> >, 
         boost::bind(foo, std::string("a"))); 
    f(); 

} // main 

А вот выход ошибки:

In file included from testcase.cpp:3: 
In file included from /usr/local/include/boost/function.hpp:64: 
In file included from /usr/local/include/boost/preprocessor/iteration/detail/iter/forward1.hpp:47: 
In file included from /usr/local/include/boost/function/detail/function_iterate.hpp:14: 
In file included from /usr/local/include/boost/function/detail/maybe_include.hpp:13: 
/usr/local/include/boost/function/function_template.hpp:132:18: error: type 'std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >' does not provide a call operator 
      return (*f)(BOOST_FUNCTION_ARGS); 
       ^~~~ 
/usr/local/include/boost/function/function_template.hpp:907:53: note: in instantiation of member function 'boost::detail::function::function_obj_invoker0<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >, std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >::invoke' requested here 
     { { &manager_type::manage }, &invoker_type::invoke }; 
                ^
/usr/local/include/boost/function/function_template.hpp:722:13: note: in instantiation of function template specialization 'boost::function0<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >::assign_to<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >' requested here 
     this->assign_to(f); 
      ^
/usr/local/include/boost/function/function_template.hpp:1042:5: note: in instantiation of function template specialization 'boost::function0<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >::function0<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >' requested here 
    base_type(f) 
    ^
/usr/local/include/boost/bind/bind.hpp:243:43: note: in instantiation of function template specialization 'boost::function<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >()>::function<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >' requested here 
     return unwrapper<F>::unwrap(f, 0)(a[base_type::a1_]); 
             ^
/usr/local/include/boost/bind/bind_template.hpp:20:27: note: in instantiation of function template specialization 'boost::_bi::list1<boost::_bi::bind_t<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >, std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > (*)(std::basic_string<char>), boost::_bi::list1<boost::_bi::value<std::basic_string<char> > > > >::operator()<boost::shared_ptr<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >, boost::shared_ptr<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > > (*)(boost::function<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >()>), boost::_bi::list0>' requested here 
     BOOST_BIND_RETURN l_(type<result_type>(), f_, a, 0); 
         ^
testcase.cpp:27:4: note: in instantiation of member function 'boost::_bi::bind_t<boost::shared_ptr<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > >, boost::shared_ptr<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > > (*)(boost::function<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >()>), boost::_bi::list1<boost::_bi::bind_t<std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > >, std::vector<std::basic_string<char>, std::allocator<std::basic_string<char> > > (*)(std::basic_string<char>), boost::_bi::list1<boost::_bi::value<std::basic_string<char> > > > > >::operator()' requested here 
    f(); 
^
1 error generated. 

Вот еще несколько подсказок. Следующий код компилируется нормально, но это не помогает мне, так как это не код, который я хочу :)

#include <boost/make_shared.hpp> 
#include <boost/shared_ptr.hpp> 
#include <boost/function.hpp> 
#include <boost/bind.hpp> 

#include <string> 
#include <vector> 

std::vector<std::string> foo() 
{ 
    std::vector<std::string> vec; 
    return vec; 
} 

template< typename T > 
boost::shared_ptr<T> ReturnValueAsShared(
    boost::function< T() > func) 
{ 
    return boost::make_shared<T>(func()); 
} 

int main() 
{ 
    auto f = boost::bind(ReturnValueAsShared< std::vector<std::string> >, 
         foo); 
    f(); 

} // main 
+3

Пожалуйста, упростите проблему и предоставите минимальный (не) рабочий пример кода. –

+0

Какая ошибка? –

+0

@KerrekSB Ошибка: std :: vector не предоставляет оператора вызова, когда я пытаюсь использовать его как 'ReturnValueAsShared >'. – Lucas

ответ

2

Некоторые конструкции (например, bind) возвращают промежуточные «выражение» типы, которые вы на самом деле не хотите, чтобы захватить на носу. В этом случае вы не должны записывать этот тип через auto, и вам может потребоваться указать явные преобразования, поскольку в противном случае не существует уникальной однооднозначной цепочки преобразования. В вашем случае, добавьте явное преобразование из выражения привязки к function:

typedef std::vector<std::string> G; 
auto f = boost::bind(ReturnValueAsShared<G>, 
      static_cast<boost::function<G()>(boost::bind(foo, std::string("a"))) 
        ); 

(само это на самом деле не работает для меня, но это не будет работать, если вы использовать соответствующий std строи ­ ЦИИ.)

+0

Обертка моего второго вызова 'bind' с литой функцией' boost :: ()> (...) 'делает все правильно. Спасибо за информацию. – Lucas

2

полностью переписан, оригинальный ответ был неверным.

Анализ ошибок

Поскольку я не знаю, в первый, что происходит здесь не так, я сделал некоторый анализ. Я держу его для дальнейшего использования; ниже описано, как избежать проблемы.

bind.hpp делает это:

return unwrapper<F>::unwrap(f, 0)(a[base_type::a1_]); 

на мой взгляд, переводит так:

unwrapper<F>::unwrap(f, 0) = ReturnValueAsShared< std::vector<std::string> > 
base_type::a1_ = boost::bind(foo, std::string("a")) 

Так что можно было бы ожидать, этот код нужно сделать, это передать аргумент функции, так как это. Но для этого для работы выражение a[base_type::a1_] должно быть типа boots:_bi::value<T>, тогда как оно имеет развернутый тип boost::_bi::bind_t. Таким образом, вместо того, чтобы функтор передается в качестве аргумента, специальная перегруженная версия вызывается:

namespace boost { namespace _bi { class list0 { 
    … 
    template<class R, class F, class L> 
    typename result_traits<R, F>::type 
    operator[] (bind_t<R, F, L> & b) const { 
     return b.eval(*this); 
    } 
    … 
} } } 

Это оценить в нульарную функции, вместо того, чтобы передать его. Поэтому вместо объекта, возвращающего вектор, аргумент теперь является vecotr. Последующие шаги попытаются преобразовать это в boost::function и провалиться.

Canonical решение

еще раз Отредактировано:
Похоже, эта специальная обработка nested binds предназначена как особенность. Говоря на #boost с пользователями Zao и heller, теперь я знаю, что есть функция protect, чтобы противостоять этим эффектам.Таким образом, каноническое решение этой проблемы, как представляется, следующее:

… 
#include <boost/bind/protect.hpp> 
… 
    auto f = boost::bind(ReturnValueAsShared< std::vector<std::string> >, 
    boost::protect (boost::bind(foo, std::string("a")))); 
… 
+0

Нет, возвращаемое значение является функтором, который при выполнении возвращает 'shared_ptr'. Если я заменил 'f()' на '(* f)()', я правильно получил ошибку компилятора, которая жалуется, что 'f' не имеет тип указателя. – Lucas

+0

ОК, Вы - проблема, проблема на одном уровне глубже. Мне кажется, что 'bind' не имеет перегрузки для' shared_ptr', поэтому он предполагает объект функции по умолчанию. Но поскольку 'shared_ptr' не предоставляет оператора вызова, все терпит неудачу. – MvG

+0

Ум, 'bind' не требует перегрузки для' shared_ptr', это шаблон. Кроме того, ошибка жалуется, что 'vector ' не имеет оператора вызова, а не 'shared_ptr'. – Lucas

3

подталкивание :: защитить это путь:

int main() 
{ 
    auto f = boost::bind(ReturnValueAsShared< std::vector<std::string> >, 
         boost::protect(boost::bind(foo, std::string("a")))); 
    f(); 

} // main 

Это настолько чист, как он может получить.

+1

Этот ответ можно улучшить, объяснив, что такое 'boost :: protect', что он делает и может быть ссылка на документы, которые описывают это еще более подробно. – Lucas