问题
I have an Event class that stores a set of tuples of weak_ptr (the observer) to a function that gets executed when the event is "fired".
The function type is: void(int &) in the example. That is to say, I want to fire the event passing a reference to a value, to have the observer change that value and to verify that the value was changed back in the observed object. The event implementation is variadic by the way, which may complicate the issue (at least the code).
At the moment this is failing. Somewhere along the line the reference is being converted to a non-reference or copied, but I cannot see where.
Full repo is below. Note assert (value != 10) fails, even though I set the value to 1 in the event handler.
#include <memory>
#include <tuple>
#include <vector>
#include <cassert>
#include <functional>
template<class FunctionPrototype>
class Event
{
public:
template<typename... Args>
void operator()(Args... args)
{
for (auto const & listener : Listeners)
{
if (auto locked = std::get<0>(listener).lock())
std::get<1>(listener)(args...);
}
}
template<typename P, typename Q, typename R, typename... Args>
void Attach(P(Q::*f)(Args...), std::shared_ptr<R> const & p)
{
auto w = std::weak_ptr<R>(p);
auto l = [w, f](Args... args) {
if (auto locked = w.lock())
return (*locked.get().*f)(args...);
else
return P();
};
Listeners.push_back(std::make_tuple(std::weak_ptr<void>(w), l));
}
typedef std::tuple<std::weak_ptr<void>, std::function<FunctionPrototype>> event_tuple;
std::vector<event_tuple> Listeners;
};
class Observed : public std::enable_shared_from_this < Observed >
{
public:
int value;
void Fire()
{
value = 10;
TheEvent(value);
assert(value != 10);
}
Event<void(int &)> TheEvent;
};
class Observer : public std::enable_shared_from_this<Observer>
{
public:
void Attach(std::shared_ptr<Observed> const & observed)
{
observed->TheEvent.Attach(&Observer::OnEvent, shared_from_this());
}
void OnEvent(int & value)
{
assert(value == 10);
value = 1;
}
};
int main(void)
{
auto observed = std::make_shared<Observed>();
auto observer1 = std::make_shared<Observer>();
observer1->Attach(observed);
observed->Fire();
return 0;
}
回答1:
Event::operator()
takes arguments by value. Rewrite it as follows:
template<typename... Args>
void operator()(Args&&... args)
{
for (auto const & listener : Listeners)
{
if (auto locked = std::get<0>(listener).lock())
std::get<1>(listener)(std::forward<Args>(args)...);
}
}
来源:https://stackoverflow.com/questions/31099155/reference-parameter-being-copied-in-variadic-template