Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

'&&' in function parameter pack

Tags:

c++

I have seen multiple instances of code where function parameter pack is declared using the && notation, as shown below, but I cannot see any advantage to using this notation.

template<typename... Args>
void Function(Args... args)
{
}

template<typename... Args>
void Function(Args&&... args)
{
}

My first thought was that the && form will be used exclusively for r-value objects, but this test proved that wrong:

struct Object
{
    // Added bodies so I see what is being called via a step-into
    Object() {}
    Object(const Object&) {}
    Object(Object&&) noexcept {}
    Object& operator=(const Object&) { return *this; }
    Object& operator=(Object&&) noexcept { return *this; }
};

Object GetObject() { Object o; return o; }

Object obj;

Function(GetObject());  
Function(GetObject());

Here, VS 2017 complains that both forms of the function are viable candidates for the call.

Can someone explain what the difference is between these two, and what advantages one may have over the other please?

like image 898
Wad Avatar asked Jan 18 '18 17:01

Wad


2 Answers

They are forwarding references in the parameter pack form. As for template parameter deduction, they can match any arguments, but the template parameter will be deduced differently comparing to the ordinary template parameter.

The major advantage of forwarding reference is that the lvalue/rvalue information will be preserved if used with std::forward. Thus they are used to "forward" something.

For example,

void real_foo(A const &a);
void real_foo(A &&a);

template<class... Args>
void foo_proxy_ordinary(Args... args) { real_foo(args...); }

template<class... Args>
void foo_proxy_perfect(Args&&... args) { real_foo(std::forward<Args>(args)...); }

The ordinary version will always call real_foo(A const &) version, because inside foo_proxy, args are always lvalue.

However, the perfect version will select real_foo(A&&) if the arguments passed in are indeed rvalues.

Combining forwarding reference with parameter pack, one can write easily generic proxy functions without performance loss in terms of lvalue/rvalue.

like image 98
llllllllll Avatar answered Oct 06 '22 12:10

llllllllll


T&& when used in the context of

template<typename T>
void f(T&& t);

is called a forwarding reference sometimes also called a universal reference.

Main advantage of a forwarding reference is that combined with std::forward it enables achieving a so-called perfect forwarding: function template passing its arguments to another function as they are (lvalue as lvalue, rvalue as rvalue).

Now it is possible to create higher-order functions that take other functions as arguments or return them, or superior function-wrappers (e.g., std::make_shared), and do other cool things.

Here is some material that explains it much better and in more detail than I possibly can:

  • Perfect forwarding and universal references in C++
  • Rvalue References and Perfect Forwarding in C++0x
  • Forwarding references proposal
  • SO: Advantages of using forward
  • SO: Perfect forwarding - what's it all about?
like image 22
AMA Avatar answered Oct 06 '22 13:10

AMA