Perfect forwarding

It is my understanding that if you use T&& and std::forward<T> with templates, you create perfect forwarding, in that you can pass both lvalues and rvalues to a single function without overloads or conversions. For a large project I've been doing, I designed the following class and I've written the following code to test it. Why doesn't it work in 3 of the 5 cases?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <iostream>
#include <functional>
#include <queue>
#include <utility>

template <typename... T>
class DelayedEvent {
private:
    std::queue<std::function<void(void)>> queuedFunctions;
	std::function<void ( T... ) > task;
public:
	DelayedEvent ( std::function<void(T...)> task ) :
		task ( task )
	{
	}

	void operator() ( T&& ... t )
	{
		queuedFunctions.push ( std::bind ( task, std::forward<T> ( t )... ) );
	}
    
    void run ()
    {
        while( !queuedFunctions.empty() ) {
            queuedFunctions.front()();
            queuedFunctions.pop();
        }
    }
};

int main()
{
    std::string nonConstString ( "nonConst" );
    const std::string constString ( "const" );
    std::string nonConstMoveString ( "nonConstMove" );
    const std::string constMoveString ( "ConstMove" );
    
    DelayedEvent<std::string> delayedEvent ( [] ( std::string text ) {
        std::cout<<text<<std::endl;
    } );
    
    //delayedEvent ( nonConstString ); error!
    //delayedEvent ( constString ); error!
    delayedEvent ( std::move ( nonConstMoveString ) );
    //delayedEvent ( std::move ( constMoveString ) ); error!
    delayedEvent ( std::string("string literal") );
    
    delayedEvent.run(); //Prints "nonConstMove" and "string literal"
    
    return 0;
} 


For the record, I'm compiling it with g++ with -std=c++1z
Last edited on
> if you use T&& and std::forward<T> with templates, you create perfect forwarding
> I designed the following class and I've written the following code to test it.

void operator() ( T&& ... t ) is not a function template; it is a non-template member function of a class template.

Make it
1
2
3
4
template < typename... U > void operator() ( U&&... t )
{
    queuedFunctions.push ( std::bind ( task, std::forward<U> ( t )... ) );
}

and the errors would go away.
Thank you, that works perfectly, and all 5 types now work. Why is it that that works?

EDIT: I actually googled it rather than just asking. Is it because I specified the template in the class as an std::string, but if it was deduced in the function call, it would have been either std::string& or std::string&&, both of which are handled by reference collapsing?
Last edited on
When we see T&&, it is a universal reference (aka forwarding reference) only if T is a deduced type.
If not, it is a plain rvalue reference.

More information: https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers


> but if it was deduced in the function call, it would have been either std::string& or std::string&&,
> both of which are handled by reference collapsing?

Yes. It would then be a deduced type.
Last edited on
Ohhhhh. That makes sense, thank you.
Topic archived. No new replies allowed.