Should my generic template use T or T &&?

I work in a file that has a given function with many overloads, for example:

inline X f(ScriptWrappable* impl, Y y, Z z) { ... } inline X f(Node* impl, Y y, Z z) { ... } inline X f(RawPtr<T> impl, Y y, Z z) { ... } inline X f(const RefPtr<T>& impl, Y y, Z z) { ... } inline X f(ScriptWrappable* impl, Y y, Z z) { ... } inline X f(const String& impl, Y y, Z z) { ... } inline X f(int64_t impl, Y y, Z z) { ... } template<typename T, size_t capacity> inline X f(const Vector<T, capacity>& impl, Y y, Z z) { ... } 

I am trying to add a new overload that requires only one parameter:

 template<typename T> inline X f(T impl, W w) { return f(impl, w->getY(), w->getZ()); } 

I use templates so that all of the above options work automatically with my new two-parameter version.

However, while looking at the code, I was asked: "Is T&& better to avoid copying?". That is, if I do instead

 template<typename T> inline X f(T&& impl, W w) { return f(impl, w->getY(), w->getZ()); } 

I do not know the answer to this question. I thought I understood universal links, but I am not familiar with them when they are good ideas or not. What would be the consequences in my situation of choosing one over the other?

If I were to guess, I would say that since the original types that T can stand, just copying (primitives, references, or pointers) T && & will not do much good. But I'm still interested, for example, any types can be transferred to the T && version, which cannot be transferred to the T version, or vice versa.

+6
source share
2 answers

You should write your function as follows:

 template<typename T> inline X f(T&& impl, W w) { return f(std::forward<T>(impl), w->getY(), w->getZ()); } 

This is called perfect forwarding. The impl type will be exactly the same as if you were directly invoking f . This will not necessarily be a reference to the r-value.

+8
source

The answer is a bit complicated because it requires some understanding of compiler rules regarding templates.

In the following declaration:

 template<class T> void func(T&& t); 

T is evaluated in the displayed context and as a result it will be processed by the compiler either as a reference to an r-value or a reference to an l-value, depending on what is appropriate. Combined with the use of std::forward (note: in this case, not std::move ) this results in perfect forwarding and is optimally efficient.

However, this is not evaluated in the derived context:

 template<class T> struct X { void foo(T&& t); }; 

In this case, foo actually requires an r-value.

Also the context is not displayed:

 template<class T> void foo(std::vector<T>&& v); 

In these two cases, you should use std::move .

+3
source

All Articles