I'm having essentially the same problem as this question, however unfortunately the only posted answer there is now a dead link.
Specifically, using VS2013 Update 4, I'm trying to get the following code to compile, and it's not being cooperative:
template<typename T, typename... Params>
auto PostWeakTask(const boost::weak_ptr<IWorkQueue>& queue,
void (T::*member)(Params...),
const boost::weak_ptr<T>& weak)
-> std::function<void (Params&&...)>
{
return [queue, member, weak](Params&&... params)
{
if (auto qp = queue.lock())
{
qp->Post([weak, params]()
{
if (auto strong = weak.lock())
{
((*strong).*member)(std::forward<Params>(params)...);
}
});
}
};
}
(As written, the capture of params fails with C3481: 'params': lambda capture variable not found. If I try using implicit capture with = instead, it says C2065: 'params' : undeclared identifier. If I try params..., I get C3521: 'params' is not a parameter pack.)
The idea of course is to return a function that when called will post a member function with arbitrary parameters to a work queue that accepts void() tasks only, and to only keep weak references to the queue and task when not yet executed.
I don't think there's anything actually wrong in the code here, though. I think I've found a workaround using bind instead of a lambda, but oddly it only seems to work with std::function and not boost::function. (Using Boost 1.55. I suspect this might be pre-rvalue-ref support?)
(On a side note, I originally tried to use decltype([](Params&...){}) as the return type, as this seems more natural. But it crashes the compiler.)
The workaround is doing something a bit weird too. Maybe I should ask this as a separate question as it mostly relates to the perfect-forwarding part instead, but:
template<typename T, typename... Params>
auto PostWeakTask(const boost::weak_ptr<IWorkQueue>& queue,
void (T::*member)(Params...),
const boost::weak_ptr<T>& weak)
-> std::function<void (Params...)>
{
struct WeakCaller
{
typedef void (T::*member_type)(Params...);
typedef boost::weak_ptr<T> weak_type;
WeakCaller(member_type member, const weak_type& weak)
: m_member(member), m_weak(weak) {}
void operator()(Params&&... params)
{
if (auto strong = m_weak.lock())
{
((*strong).*m_member)(std::forward<Params>(params)...);
}
}
private:
member_type m_member;
weak_type m_weak;
};
return [=](Params&&... params)
{
if (auto qp = queue.lock())
{
qp->Post(std::bind(WeakCaller(member, weak),
std::forward<Params>(params)...));
}
}
}
This seems like it should work, but when I try calling it (with a void (tribool,tribool,const std::string&) method) I get a binding error that a tribool parameter is not compatible with a tribool&& one.
(Specifically: C2664: 'void WeakCaller<T,boost::logic::tribool,boost::logic::tribool,const std::string &>::operator ()(boost::logic::tribool &&,boost::logic::tribool &&,const std::string &)' : cannot convert argument 1 from 'boost::logic::tribool' to 'boost::logic::tribool &&'.)
I thought part of the point of using rvalue references this way was that they were supposed to forward perfectly without needing multiple overloads?
I can make it compile by making WeakCaller have void operator()(Params... params) instead, but doesn't this defeat perfect forwarding? (Oddly it seems ok with leaving the && at the top-level lambda... and I'm not sure if the mismatch between the std::function signature and the lambda signature is ok.)
Aucun commentaire:
Enregistrer un commentaire