我尝试使用VC11和g ++ 4.7.2编译以下示例:
#include <functional>
class X {
public:
template <typename T>
explicit X(T t)
{
std::bind(&X::invoke<T>, this, t)();
}
private:
template <typename T>
void invoke(T t)
{
t();
}
};
class Y {
public:
void foo() {
//...
}
};
int main() {
Y y;
X x(std::bind(&Y::foo, &y));
return 0;
}
但它完成了错误。我不确定粘贴整个编译器输出是否合理,但通常是
vc11说:
错误C2664:'void std :: _ Pmf_wrap :: operator()(_ Farg0&amp;,_ V0_t)const':无法将参数3从'void'转换为'std :: _ Bind,Y *,std :: _ Nil,的std :: _无,性病:: _无,性病:: _无,性病:: _无,性病:: _无&GT;” c:\ program files(x86)\ microsoft visual studio 11.0 \ vc \ include \ functional 1152 1 ConsoleApplication1(Microsoft Visual C ++编译器2012年11月CTP)
和g ++:
编译完成但有错误:
source.cpp:实例化'X :: X(T)[带T = std :: _ Bind(Y *)&gt;]':
source.cpp:28:33:从这里要求
source.cpp:8:9:错误:无法调用'(std :: _ Bind_helper(Y *)&gt;),X * const,std :: _ Bind(Y *)&gt;&amp;&gt; :: type {aka std :: _ Bind(Y *)&gt;)&gt;(X *,std :: _ Bind(Y *)&gt;)&gt;})()'
有没有办法解决这个问题。保存主要思想对于我来说非常重要 - 可以使用任何可调用对象(函数对象,函数指针或std::bind()
函数返回的调用包装器)实例化的类。
如果有人帮忙,我将不胜感激。
P.S。如果我创建X
的实例,它会传递函数对象或函数指针。
答案 0 :(得分:4)
我认为他们在boost::bind
中使用std::bind
时忽略了boost::protect()
的重要部分,即#include <boost/bind/protect.hpp>
// ...
X x(boost::protect(std::bind(&Y::foo, &y)));
。您的代码可以修改如下:
template <typename T>
explicit X(T t)
{
auto tt = boost::protect(t);
auto f = std::bind(&X::invoke<decltype(tt)>, this, tt);
f();
}
或者,或者:
template <typename T>
explicit X(T t)
{
auto f = [t, this]() { this->invoke(t); };
f();
}
// ...
X x([&y](){ y.foo(); });
请参阅http://www.boost.org/doc/libs/1_53_0/libs/bind/bind.html
虽然默认情况下第一个参数未被评估,但所有其他参数都是。有时,有必要不评估第一个之后的参数,即使它们是嵌套的绑定子表达式。这可以通过另一个掩盖类型的函数对象protect来实现,这样bind就无法识别和评估它。调用时,protect会简单地将参数列表转发给未修改的其他函数对象。
标头boost / bind / protect.hpp包含一个protect实现。要保护绑定函数对象不受评估,请使用protect(bind(f,...))。
Scott Meyers即将发布的Effective C++11: Content and Status将推荐首选lambdas到std :: bind 。在C ++ 11中,您可以简单地执行:
{{1}}
答案 1 :(得分:2)
问题的根本原因似乎是std::bind
执行其参数的内部复制,特别是t
。
您可能希望以这种方式解决方法:
template <typename T>
explicit X(T t)
{
std::bind(&X::invoke<T>, this, std::placeholders::_1)(t);
// ^^^^^^^^^^^^^^^^^^^^^ ^
}
这也可行,但你不会让bind
的结果比论据t
更长(否则,你会传递一个悬挂的引用{{1} }):
invoke<T>()
<强>更新强>
上述解决方案是有助于实现您在示例中显示的内容的解决方法。但是,从评论中可以看出,您的用例可能大不相同(例如,传递 template <typename T>
explicit X(T t)
{
std::bind(&X::invoke<T>, this, cref(t))();
// ^^^^^^^
}
的结果以供以后评估)。
正如Maxim Yegorushkin在他的回答中正确指出的那样,概念上正确的解决方案在于使用诸如Boost&#39; bind
之类的构造。
如果您不想使用Boost,使用C ++ 11的可变参数模板定义您自己的protect
函数非常容易:
protect()
最终,这就是你在课堂上使用它的方式,正如Maxim建议的那样:
// Imitates boost::protect, but with variadic templates and perfect forwarding
namespace detail
{
template<typename F>
struct protect
{
private:
F _f;
public:
explicit protect(F f): _f(f)
{
}
template<typename... Ts>
auto operator () (Ts&&... args) ->
decltype(_f(std::forward<Ts>(args)...))
{
return _f(std::forward<Ts>(args)...);
}
};
}
template<typename F>
detail::protect<F> protect(F&& f)
{
return detail::protect<F>(std::forward<F>(f));
}