Perfect forwarding, further questions.

Started by
1 comment, last by Aardvajk 7 years, 3 months ago
Thought better to start a new thread rather than continue my recent one since my question has changed slightly. Hope that is okay.

I was asking earlier about the difference between

template<typename... Args> void f(Args... args){ call(args...); }
and

template<typename... Args> void f(Args&&... args){ call(std::forward<Args>(args)...); }
For which I got a good response and a link to an article that sort of made sense. Upshot was that the second version was to be preferred.

So I went to update some of my library. First of all, I had a perfect forwarding constructor to a class which I changed and this works fine:

template<typename T> class BlendValue
{
public:
    template<typename... Args> BlendValue(Args&&... args) : v(std::forward<Args>(args)...), o(v) { }
};
However, I also have a signals/slots implementation which tries to use perfect forwarding for the call of the signal. My previous version looked like this:

template<typename... Args> void Signal<Args...>::operator()(Args... args)
{
	//snip
	for(auto &n: v) n->call(args...); // call back the slots
	//snip
}
When I changed this to:

template<typename... Args> void Signal<Args...>::operator()(Args&&... args)
{
	//snip
	for(auto &n: v) n->call(std::forward<Args>(args)...); // call back the slots
	//snip
}
elsewhere in the library, where I declare a signal simply as:

Signal<bool> activated;

void ApplicationEvents::onActivated(bool state)
{
	activated(state);
}
The compiler (GCC) is throwing an error:

cannot bind 'bool' lvalue to 'bool&&'
Now, when I use my original code, without the && and the std::forward<>, everything works. I've tested and I can create signals and trigger them with value types, reference types, even const reference types that can be given temporaries as the call parameter. So the original version works and does everything I want.

But it is all contrary to the expert advice I am being given, so I wondered if anyone could shed any light on the compiler error I am getting and explain what is happening here?

Thanks in advance.
Advertisement
Now, when I use my original code, without the && and the std::forward<>, everything works. I've tested and I can create signals and trigger them with value types, reference types, even const reference types that can be given temporaries as the call parameter. So the original version works and does everything I want.

You have a different case in both examples. As has been explained in your last threads, this uses a special kind of template-deduction, called "universal references",which apply here:


template<typename T> class BlendValue
{
public:
template<typename... Args> BlendValue(Args&&... args) : v(std::forward<Args>(args)...), o(v) { }
};

'This is due to that "Args" is deduced by what you pass to the function.

Now in case of a signal, "args" is part of the class-template-params. When you use Args&&... args, in a function, "Args" are not deduced, so the compiler just makes rvalue references out of it...


template<typename ... Args>
class Signal
{
    void operator(Args&&... args)
    {
    }
}

Signal<bool, int> sig; // Args = bool, int

bool b = true;
int i = 1;
sig(b, i); => sig::operator=()(bool&& bool, int&& int);

Note that even if this would work, you'd probably don't want universal/rvalue references for signal/slots anyways. Since multiple things can respond to the signal there is not use for forwarding (which would mean/could lead to the first receiver of the signal moving the value of one of the attributes away). You'd just want to manually specify what you hand over in the signal:


sig<bool> => operator()(bool)
sig<const std::string&> => operator()(const std::string&)
sig<MyClass*> => operator()(MyClass*)

So then there is no use for "Args&&... args" in the operator, so you can just put "Args... args", because that will have the correct semantic anyways, as you can see above.

;TL;DR: You don't even need perfect forwarding here.

Thanks Juliean, all I needed to know.

- Paul

This topic is closed to new replies.

Advertisement