我正在尝试编写一个函数,该函数将接受函数器作为参数,调用函数器,然后返回包装在boost::shared_ptr
中的返回值。
下面的代码拒绝编译,我完全没有想法了。我得到"std::vector< std::string >没有提供调用操作符“(粗略地)。我在Mac上使用的是Clang 3.1。
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 >] ) ) );
编辑:这是一个完整的自包含测试用例。由于同样的错误,这段代码无法编译,我无论如何也看不出有什么问题:
#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
发布于 2012-07-16 06:53:45
一些构造(比如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
构造,它确实能起作用。)
发布于 2012-07-16 16:53:47
boost::protect是最好的选择:
int main()
{
auto f = boost::bind( ReturnValueAsShared< std::vector< std::string > >,
boost::protect(boost::bind( foo, std::string("a") ) ) );
f();
} // main
这是它能得到的最干净的东西。
发布于 2012-07-16 04:42:41
完全重写,原始答案不正确。
误差分析
因为我一开始不知道这里出了什么问题,所以我做了一些分析。我将其保留下来以备将来参考;有关如何避免该问题,请参阅下面的解决方案。
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);
}
…
} } }
这将计算空值函数,而不是传递它。因此,现在的参数不再是返回向量的对象,而是一个向量。后续步骤将尝试将其转换为boost::function
,但会失败。
正则解
再次编辑:
看起来这种对nested binds的特殊处理是作为一项功能而设计的。与用户Zao和heller在#boost
上交谈时,我现在知道有一个protect
函数可以消除这些影响。因此,这个问题的规范解决方案似乎如下所示:
…
#include <boost/bind/protect.hpp>
…
auto f = boost::bind( ReturnValueAsShared< std::vector< std::string > >,
boost::protect ( boost::bind( foo, std::string("a") ) ) );
…
https://stackoverflow.com/questions/11494751
复制相似问题