C++ Templates: Automatically overload templated function with const& for constant params? - c++

Suppose I have a template function foo() that takes two integer references as parameters. I'd like the template function to also automatically handle constant references (such as those from constants). Here is a generalized example. I can get foo() to work, but I have to provide a new implementation for every permutation of reference/const-reference parameters.
#include <iostream>
using namespace std;
template<typename A, typename B>
void foo(A& a, B& b)
{
cout<<"a:"<<a<<" b:"<<b<<endl;
}
template<typename A, typename B>
void foo(A& a, const B& b)
{
cout<<"a:"<<a<<" b:"<<b<<endl;
}
template<typename A, typename B>
void foo(const A& a, B& b)
{
cout<<"a:"<<a<<" b:"<<b<<endl;
}
template<typename A, typename B>
void foo(const A& a, const B& b)
{
cout<<"a:"<<a<<" b:"<<b<<endl;
}
int main()
{
int x = 0;
foo(x, x);
foo(x, 10);
foo(10, x);
foo(20, 20);
return 0;
}
The above example is a little bit contrived, but it is a generalization of what I am trying to do. In my more complex case, I have a class that acts as a wrapper to a set of parameters. The class constructor is templated, like foo(), and can have as many as 10 parameters. It would be a nightmare to enumerate all 2^10 possible constructors.

The problem that you describe is perfect forwarding problem. C++11 solved this problem with universal references:
template<typename A, typename B>
void foo(A&& a, B&& b) {
bar(std::forward<A>(a), std::forward<B>(b));
}
Parameters here are not rvalue references, but universal references. They will have the same ref-ness and const-ness as arguments.
If arguments are rvalues, in foo parameters will be rvalues with names. Named rvalues are lvalues. To pass parameters to sub-functions with preserved value-ness, you need to wrap them in std::forward. Function bar will get a and b with exactly the same type as foo.

If the template is not going to modify the arguments, then just offer the version with the const& and you should be fine:
template<typename A, typename B>
void foo(const A& a, const B& b)
{
cout<<"a:"<<a<<" b:"<<b<<endl;
}
If you pass a non-const lvalue, it will still be bound by a const reference and everything will work.
If you want some of the overloads to modify the arguments, then rethink the design, as those don't seem like functions that should share a name. There are exceptions, for examples, accessors into internal members of a structure, where you might want to return a const& if the object is const or a non-const reference otherwise... If that is the case, you can go the opposite way and offer only the non-const overload:
template<typename A, typename B>
void foo(A& a, B& b)
In this case, if the argument is a temporary or a non-const reference, the deduce type will reflect it and it will bind the argument with a const&.
int main() {
int a = 5;
const int b = 10;
foo(a,b); // foo<int,const int>(int&,const int&)
foo(10,b); // foo<const int,const int>(const int&, const int&)
}
Rereading your question it seems that you might be interested in perfect forwarding (this might or not fit your bill). If that is the case, and if you have a C++11 compiler you can use universal-references with a variadic template. Building a good wrapper is a hard thing, although you might be able to just use std::tuple as the actual storage, which should make the task quite simple.

Apart from all the answers posted which are correct, here is a simple program just for your reference as this might help you to understand the concept better.
#include<iostream>
template<class X>
void func(X& x, X& y)
{
//your code
}
template<class X, class Y>
void intermediateFunc(X&& x, Y&& y)
{
func(x,y);
}
int main()
{
int y = 9;
intermediateFunc(5,5);
intermediateFunc(y,5);
intermediateFunc(5,y);
intermediateFunc(y,y);
}

Related

What is the proper way to declare a function that can take either rvalue or lvalue reference?

Suppose I want to have a function foo taking either lvalue or rvalue reference as the parameter.
I could break it into two overloads taking lvalue and rvalue references.
void foo(int& a){/*some impl*/} // lvalue ref
void foo(int&& a){foo(a);} // rvalue ref
int main(){
int a;
foo(a); // lvalue
foo(1); // rvalue
}
It does work but is pretty verbose. I could improve it using templates.
template<typename T>
void foo(T &&a) { /*some impl*/ }
int main(){
int a;
foo(a); // lvalue, T = int&
foo(1); // rvalue, T = int
}
For a binary function the template would need to take two template parameters.
template<typename T1, typename T2>
void foo(T1 &&a, T2 &&b) { /*some impl*/ }
int main(){
int a;
foo(a, a); // (lvalue, lvalue), T1 = int&, T2 = int&
foo(1, 1); // (rvalue, rvalue), T1 = int, T2 = int
foo(1, a); // (rvalue, lvalue), T1 = int, T2 = int&
foo(a, 1); // (lvalue, rvalue), T1 = int&, T2 = int
}
Is it the way to go? Is there any better way?
I have hardly any experience using cpp but it seems suspicious that I need to do such tricks to simply say "do not make copies of passed parameters".
I am using gcc 5.4.0 with -std=c++11.
-- UPDATE 1 --
I came up with the question when I was using streams library range(T&& lower, T&& upper) method which takes both T&& parameters. I can pass both lvalue or rvalue parameters to the function but it makes me unable to pass for example 0 and some_var as parameters. Whatever is the author's reasons for the function taking T&& parameters I was wondering if there is a way to extend the declaration to take mixed lvalue/rvalue parameters without sacrificing whatever the author wanted to achieve.
-- UPDATE 2 --
If the parameter is read-only const & can be used (#RichardCritten).
When you want to modify/return the parameter without copying you can use either templates or #Yakk solution.
#Yakk solution seems better when you declare a function that takes multiple parameters.
For example, if a function takes two int l/r-value reference parameters and returns int reference using templates leads to a messy signature.
template<typename T1, typename T2>
int& foo(T1 &&a, T2 &&b) {
a += b;
return a;
}
While #Yakk solution gives a pretty elegant one.
int& foo(any_ref<int> a, any_ref<int> b) {
a += b;
return a;
}
template<class T>
struct any_ref{
T& t;
any_ref(T&in):t(in){}
any_ref(T&&in):any_ref(in){}
any_ref(any_ref const&)=default;
any_ref& operator=(any_ref const&)=default;
~any_ref()=default;
operator T&()const&{return t;}
operator T&&()&&{return std::move(t);}// maybe
T& get()const{return *this;}
};
Then:
void foo(any_ref<int>a, any_ref<int>b);
is not a template function. The body can do a int&a=a_arg; if it wants real references.
But really, just take int& if you want a "maybe-out" parameter. Callers who want to pass an rvalue as a lvalue can write:
templare<class T>
T& as_lvalue(T&&t){return t;}
and cast their rvalueness away.
If the parameter is a read only reference, use const&.

Do I need T&& and std::forward when writing a templated `<<` operator? [duplicate]

In perfect forwarding, std::forward is used to convert the named rvalue references t1 and t2 to unnamed rvalue references. What is the purpose of doing that? How would that affect the called function inner if we leave t1 & t2 as lvalues?
template <typename T1, typename T2>
void outer(T1&& t1, T2&& t2)
{
inner(std::forward<T1>(t1), std::forward<T2>(t2));
}
You have to understand the forwarding problem. You can read the entire problem in detail, but I'll summarize.
Basically, given the expression E(a, b, ... , c), we want the expression f(a, b, ... , c) to be equivalent. In C++03, this is impossible. There are many attempts, but they all fail in some regard.
The simplest is to use an lvalue-reference:
template <typename A, typename B, typename C>
void f(A& a, B& b, C& c)
{
E(a, b, c);
}
But this fails to handle temporary values (rvalues): f(1, 2, 3);, as those cannot be bound to an lvalue-reference.
The next attempt might be:
template <typename A, typename B, typename C>
void f(const A& a, const B& b, const C& c)
{
E(a, b, c);
}
Which fixes the above problem because "const X& binds to everything", including both lvalues and rvalues, but this causes a new problem. It now fails to allow E to have non-const arguments:
int i = 1, j = 2, k = 3;
void E(int&, int&, int&);
f(i, j, k); // oops! E cannot modify these
The third attempt accepts const-references, but then const_cast's the const away:
template <typename A, typename B, typename C>
void f(const A& a, const B& b, const C& c)
{
E(const_cast<A&>(a), const_cast<B&>(b), const_cast<C&>(c));
}
This accepts all values, can pass on all values, but potentially leads to undefined behavior:
const int i = 1, j = 2, k = 3;
E(int&, int&, int&);
f(i, j, k); // ouch! E can modify a const object!
A final solution handles everything correctly...at the cost of being impossible to maintain. You provide overloads of f, with all combinations of const and non-const:
template <typename A, typename B, typename C>
void f(A& a, B& b, C& c);
template <typename A, typename B, typename C>
void f(const A& a, B& b, C& c);
template <typename A, typename B, typename C>
void f(A& a, const B& b, C& c);
template <typename A, typename B, typename C>
void f(A& a, B& b, const C& c);
template <typename A, typename B, typename C>
void f(const A& a, const B& b, C& c);
template <typename A, typename B, typename C>
void f(const A& a, B& b, const C& c);
template <typename A, typename B, typename C>
void f(A& a, const B& b, const C& c);
template <typename A, typename B, typename C>
void f(const A& a, const B& b, const C& c);
N arguments require 2N combinations, a nightmare. We'd like to do this automatically.
(This is effectively what we get the compiler to do for us in C++11.)
In C++11, we get a chance to fix this. One solution modifies template deduction rules on existing types, but this potentially breaks a great deal of code. So we have to find another way.
The solution is to instead use the newly added rvalue-references; we can introduce new rules when deducing rvalue-reference types and create any desired result. After all, we cannot possibly break code now.
If given a reference to a reference (note reference is an encompassing term meaning both T& and T&&), we use the following rule to figure out the resulting type:
"[given] a type TR that is a reference to a type T, an attempt to create the type “lvalue reference to cv TR” creates the type “lvalue reference to T”, while an attempt to create the type “rvalue reference to cv TR” creates the type TR."
Or in tabular form:
TR R
T& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T& && -> T& // rvalue reference to cv TR -> TR (lvalue reference to T)
T&& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T&& && -> T&& // rvalue reference to cv TR -> TR (rvalue reference to T)
Next, with template argument deduction: if an argument is an lvalue A, we supply the template argument with an lvalue reference to A. Otherwise, we deduce normally. This gives so-called universal references (the term forwarding reference is now the official one).
Why is this useful? Because combined we maintain the ability to keep track of the value category of a type: if it was an lvalue, we have an lvalue-reference parameter, otherwise we have an rvalue-reference parameter.
In code:
template <typename T>
void deduce(T&& x);
int i;
deduce(i); // deduce<int&>(int& &&) -> deduce<int&>(int&)
deduce(1); // deduce<int>(int&&)
The last thing is to "forward" the value category of the variable. Keep in mind, once inside the function the parameter could be passed as an lvalue to anything:
void foo(int&);
template <typename T>
void deduce(T&& x)
{
foo(x); // fine, foo can refer to x
}
deduce(1); // okay, foo operates on x which has a value of 1
That's no good. E needs to get the same kind of value-category that we got! The solution is this:
static_cast<T&&>(x);
What does this do? Consider we're inside the deduce function, and we've been passed an lvalue. This means T is a A&, and so the target type for the static cast is A& &&, or just A&. Since x is already an A&, we do nothing and are left with an lvalue reference.
When we've been passed an rvalue, T is A, so the target type for the static cast is A&&. The cast results in an rvalue expression, which can no longer be passed to an lvalue reference. We've maintained the value category of the parameter.
Putting these together gives us "perfect forwarding":
template <typename A>
void f(A&& a)
{
E(static_cast<A&&>(a));
}
When f receives an lvalue, E gets an lvalue. When f receives an rvalue, E gets an rvalue. Perfect.
And of course, we want to get rid of the ugly. static_cast<T&&> is cryptic and weird to remember; let's instead make a utility function called forward, which does the same thing:
std::forward<A>(a);
// is the same as
static_cast<A&&>(a);
I think to have a conceptual code implementing std::forward can help with the understanding. This is a slide from Scott Meyers talk An Effective C++11/14 Sampler
Function move in the code is std::move. There is a (working) implementation for it earlier in that talk. I found actual implementation of std::forward in libstdc++, in file move.h, but it is not at all instructive.
From a user's perspective, the meaning of it is that std::forward is a conditional cast to an rvalue. It can be useful if I am writing a function which expects either an lvalue or rvalue in a parameter and wants to pass it to another function as an rvalue only if it was passed in as an rvalue. If I did not wrap the parameter in std::forward, it would be always passed as a normal reference.
#include <iostream>
#include <string>
#include <utility>
void overloaded_function(std::string& param) {
std::cout << "std::string& version" << std::endl;
}
void overloaded_function(std::string&& param) {
std::cout << "std::string&& version" << std::endl;
}
template<typename T>
void pass_through(T&& param) {
overloaded_function(std::forward<T>(param));
}
int main() {
std::string pes;
pass_through(pes);
pass_through(std::move(pes));
}
Sure enough, it prints
std::string& version
std::string&& version
The code is based on an example from the previously mentioned talk. Slide 10, at about 15:00 from the start.
In perfect forwarding, std::forward is used to convert the named rvalue reference t1 and t2 to unnamed rvalue reference. What is the purpose of doing that? How would that effect the called function inner if we leave t1 & t2 as lvalue?
template <typename T1, typename T2> void outer(T1&& t1, T2&& t2)
{
inner(std::forward<T1>(t1), std::forward<T2>(t2));
}
If you use a named rvalue reference in an expression it is actually an lvalue (because you refer to the object by name). Consider the following example:
void inner(int &, int &); // #1
void inner(int &&, int &&); // #2
Now, if we call outer like this
outer(17,29);
we would like 17 and 29 to be forwarded to #2 because 17 and 29 are integer literals and as such rvalues. But since t1 and t2 in the expression inner(t1,t2); are lvalues, you'd be invoking #1 instead of #2. That's why we need to turn the references back into unnamed references with std::forward. So, t1 in outer is always an lvalue expression while forward<T1>(t1) may be an rvalue expression depending on T1. The latter is only an lvalue expression if T1 is an lvalue reference. And T1 is only deduced to be an lvalue reference in case the first argument to outer was an lvalue expression.
How would that affect the called function inner if we leave t1 & t2 as lvalue?
If, after instantiating, T1 is of type char, and T2 is of a class, you want to pass t1 per copy and t2 per const reference. Well, unless inner() takes them per non-const reference, that is, in which case you want to do so, too.
Try to write a set of outer() functions which implement this without rvalue references, deducing the right way to pass the arguments from inner()'s type. I think you'll need something 2^2 of them, pretty hefty template-meta stuff to deduce the arguments, and a lot of time to get this right for all cases.
And then someone comes along with an inner() that takes arguments per pointer. I think that now makes 3^2. (Or 4^2. Hell, I can't be bothered to try to think whether const pointer would make a difference.)
And then imagine you want to do this for a five parameters. Or seven.
Now you know why some bright minds came up with "perfect forwarding": It makes the compiler do all this for you.
A point that hasn't been made crystal clear is that static_cast<T&&> handles const T& properly too.
Program:
#include <iostream>
using namespace std;
void g(const int&)
{
cout << "const int&\n";
}
void g(int&)
{
cout << "int&\n";
}
void g(int&&)
{
cout << "int&&\n";
}
template <typename T>
void f(T&& a)
{
g(static_cast<T&&>(a));
}
int main()
{
cout << "f(1)\n";
f(1);
int a = 2;
cout << "f(a)\n";
f(a);
const int b = 3;
cout << "f(const b)\n";
f(b);
cout << "f(a * b)\n";
f(a * b);
}
Produces:
f(1)
int&&
f(a)
int&
f(const b)
const int&
f(a * b)
int&&
Note that 'f' has to be a template function. If it's just defined as 'void f(int&& a)' this doesn't work.
It may be worthwhile to emphasize that forward has to be used in tandem with an outer method with forwarding/universal reference. Using forward by itself as the following statements is allowed, but does no good other than causing confusion. The standard committee may want to disable such flexibility otherwise why don't we just use static_cast instead?
std::forward<int>(1);
std::forward<std::string>("Hello");
In my opinion, move and forward are design patterns which are natural outcomes after r-value reference type is introduced. We should not name a method assuming it is correctly used unless incorrect usage is forbidden.
From another viewpoint, when dealing with rvalues in a universal reference assignment, it may be desirable to preserve the type of a variable as it is. For example
auto&& x = 2; // x is int&&
auto&& y = x; // But y is int&
auto&& z = std::forward<decltype(x)>(x); // z is int&&
Using std::forward, we ensured z exactly has the same type as x.
Moreover, std::forward doesn't affect lvalue references:
int i;
auto&& x = i; // x is int&
auto&& y = x; // y is int&
auto&& z = std::forward<decltype(x)>(x); // z is int&
Still z has the same type as x.
So, back to your case, if the inner function has two overloads for int& and int&&, you want to pass variables like z assignment not y one.
The types in the example can be assessed via:
std::cout<<is_same_v<int&,decltype(z)>;
std::cout<<is_same_v<int&&,decltype(z)>;

Returning value with the same constness as the parameter

I have a function with these overloads:
A& f(B& b)
{
return b.a;
}
const A& f(const B& b)
{
return b.a;
}
The return statement can be a much more complicated logic which returns some value that is const if b is const. For example, B is a container of containers (possibly with even deeper nesting) and we are searching for one element. In this case, copying the function is not a good idea. Is there any alternatives for achieving the same result?
I can think of some solutions, but I don't really like them.
template <typename T>
auto f(T& b) -> decltype(b.a)
{
return b.a;
}
It works but becomes complicated if that b.a is not so trivial (as in the with in the containers).
A& f(B& b)
{
return b.a;
}
const A& f(const B& b)
{
return f(const_cast<B&>(b));
}
This also works but if feels like a hack. Is there a simple and clean solution?
I would implement non-const version in terms of const version, not the other way round (as you did).
A& f(B& b)
{
return const_cast<A&>(f(static_cast<B const&>(b)));
}
It looks a bit safer from mutability point of view.
As for the template version, how about this:
template <typename T>
auto f(T& b) -> std::conditional_t<std::is_const<T>{}, A const&, A&>
{
return b.a;
}
If std::conditional_t (C++14) is not supported, then use std::conditional:
typename std::conditional<std::is_const<T>{}, A const&, A&>::type
Hope that helps.
A possible solution:
template<typename B>
struct Return_type { typedef A &Type; };
template<typename B>
struct Return_type<B const &> { typedef A const &Type; };
template<typename B>
typename Return_type<B>::Type f(B &&b)
{
return b.a;
}
EDIT:
C++14 will have function return type deduction, so this should work:
template<typename T>
auto &f(T &&b)
{
return b.a;
}
gcc 4.8.1 already compiles it.
If you know that f doesn't mutate the data, then the const_cast is not a hack, but rather a perfectly legitimate solution.
The typical example for this is strchr, which finds a character in a string. We know that this doesn't mutate, but it's perfectly sensible to allow a mutable string to be mutated by offering a non-const overload. However, the non-const version can happily be implemented in terms of the const overload.
Bear in mind that it's not a violation to use const_cast. It's only a violation to mutate a constant object.

How does perfect forwarding work? [duplicate]

In perfect forwarding, std::forward is used to convert the named rvalue references t1 and t2 to unnamed rvalue references. What is the purpose of doing that? How would that affect the called function inner if we leave t1 & t2 as lvalues?
template <typename T1, typename T2>
void outer(T1&& t1, T2&& t2)
{
inner(std::forward<T1>(t1), std::forward<T2>(t2));
}
You have to understand the forwarding problem. You can read the entire problem in detail, but I'll summarize.
Basically, given the expression E(a, b, ... , c), we want the expression f(a, b, ... , c) to be equivalent. In C++03, this is impossible. There are many attempts, but they all fail in some regard.
The simplest is to use an lvalue-reference:
template <typename A, typename B, typename C>
void f(A& a, B& b, C& c)
{
E(a, b, c);
}
But this fails to handle temporary values (rvalues): f(1, 2, 3);, as those cannot be bound to an lvalue-reference.
The next attempt might be:
template <typename A, typename B, typename C>
void f(const A& a, const B& b, const C& c)
{
E(a, b, c);
}
Which fixes the above problem because "const X& binds to everything", including both lvalues and rvalues, but this causes a new problem. It now fails to allow E to have non-const arguments:
int i = 1, j = 2, k = 3;
void E(int&, int&, int&);
f(i, j, k); // oops! E cannot modify these
The third attempt accepts const-references, but then const_cast's the const away:
template <typename A, typename B, typename C>
void f(const A& a, const B& b, const C& c)
{
E(const_cast<A&>(a), const_cast<B&>(b), const_cast<C&>(c));
}
This accepts all values, can pass on all values, but potentially leads to undefined behavior:
const int i = 1, j = 2, k = 3;
E(int&, int&, int&);
f(i, j, k); // ouch! E can modify a const object!
A final solution handles everything correctly...at the cost of being impossible to maintain. You provide overloads of f, with all combinations of const and non-const:
template <typename A, typename B, typename C>
void f(A& a, B& b, C& c);
template <typename A, typename B, typename C>
void f(const A& a, B& b, C& c);
template <typename A, typename B, typename C>
void f(A& a, const B& b, C& c);
template <typename A, typename B, typename C>
void f(A& a, B& b, const C& c);
template <typename A, typename B, typename C>
void f(const A& a, const B& b, C& c);
template <typename A, typename B, typename C>
void f(const A& a, B& b, const C& c);
template <typename A, typename B, typename C>
void f(A& a, const B& b, const C& c);
template <typename A, typename B, typename C>
void f(const A& a, const B& b, const C& c);
N arguments require 2N combinations, a nightmare. We'd like to do this automatically.
(This is effectively what we get the compiler to do for us in C++11.)
In C++11, we get a chance to fix this. One solution modifies template deduction rules on existing types, but this potentially breaks a great deal of code. So we have to find another way.
The solution is to instead use the newly added rvalue-references; we can introduce new rules when deducing rvalue-reference types and create any desired result. After all, we cannot possibly break code now.
If given a reference to a reference (note reference is an encompassing term meaning both T& and T&&), we use the following rule to figure out the resulting type:
"[given] a type TR that is a reference to a type T, an attempt to create the type “lvalue reference to cv TR” creates the type “lvalue reference to T”, while an attempt to create the type “rvalue reference to cv TR” creates the type TR."
Or in tabular form:
TR R
T& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T& && -> T& // rvalue reference to cv TR -> TR (lvalue reference to T)
T&& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T&& && -> T&& // rvalue reference to cv TR -> TR (rvalue reference to T)
Next, with template argument deduction: if an argument is an lvalue A, we supply the template argument with an lvalue reference to A. Otherwise, we deduce normally. This gives so-called universal references (the term forwarding reference is now the official one).
Why is this useful? Because combined we maintain the ability to keep track of the value category of a type: if it was an lvalue, we have an lvalue-reference parameter, otherwise we have an rvalue-reference parameter.
In code:
template <typename T>
void deduce(T&& x);
int i;
deduce(i); // deduce<int&>(int& &&) -> deduce<int&>(int&)
deduce(1); // deduce<int>(int&&)
The last thing is to "forward" the value category of the variable. Keep in mind, once inside the function the parameter could be passed as an lvalue to anything:
void foo(int&);
template <typename T>
void deduce(T&& x)
{
foo(x); // fine, foo can refer to x
}
deduce(1); // okay, foo operates on x which has a value of 1
That's no good. E needs to get the same kind of value-category that we got! The solution is this:
static_cast<T&&>(x);
What does this do? Consider we're inside the deduce function, and we've been passed an lvalue. This means T is a A&, and so the target type for the static cast is A& &&, or just A&. Since x is already an A&, we do nothing and are left with an lvalue reference.
When we've been passed an rvalue, T is A, so the target type for the static cast is A&&. The cast results in an rvalue expression, which can no longer be passed to an lvalue reference. We've maintained the value category of the parameter.
Putting these together gives us "perfect forwarding":
template <typename A>
void f(A&& a)
{
E(static_cast<A&&>(a));
}
When f receives an lvalue, E gets an lvalue. When f receives an rvalue, E gets an rvalue. Perfect.
And of course, we want to get rid of the ugly. static_cast<T&&> is cryptic and weird to remember; let's instead make a utility function called forward, which does the same thing:
std::forward<A>(a);
// is the same as
static_cast<A&&>(a);
I think to have a conceptual code implementing std::forward can help with the understanding. This is a slide from Scott Meyers talk An Effective C++11/14 Sampler
Function move in the code is std::move. There is a (working) implementation for it earlier in that talk. I found actual implementation of std::forward in libstdc++, in file move.h, but it is not at all instructive.
From a user's perspective, the meaning of it is that std::forward is a conditional cast to an rvalue. It can be useful if I am writing a function which expects either an lvalue or rvalue in a parameter and wants to pass it to another function as an rvalue only if it was passed in as an rvalue. If I did not wrap the parameter in std::forward, it would be always passed as a normal reference.
#include <iostream>
#include <string>
#include <utility>
void overloaded_function(std::string& param) {
std::cout << "std::string& version" << std::endl;
}
void overloaded_function(std::string&& param) {
std::cout << "std::string&& version" << std::endl;
}
template<typename T>
void pass_through(T&& param) {
overloaded_function(std::forward<T>(param));
}
int main() {
std::string pes;
pass_through(pes);
pass_through(std::move(pes));
}
Sure enough, it prints
std::string& version
std::string&& version
The code is based on an example from the previously mentioned talk. Slide 10, at about 15:00 from the start.
In perfect forwarding, std::forward is used to convert the named rvalue reference t1 and t2 to unnamed rvalue reference. What is the purpose of doing that? How would that effect the called function inner if we leave t1 & t2 as lvalue?
template <typename T1, typename T2> void outer(T1&& t1, T2&& t2)
{
inner(std::forward<T1>(t1), std::forward<T2>(t2));
}
If you use a named rvalue reference in an expression it is actually an lvalue (because you refer to the object by name). Consider the following example:
void inner(int &, int &); // #1
void inner(int &&, int &&); // #2
Now, if we call outer like this
outer(17,29);
we would like 17 and 29 to be forwarded to #2 because 17 and 29 are integer literals and as such rvalues. But since t1 and t2 in the expression inner(t1,t2); are lvalues, you'd be invoking #1 instead of #2. That's why we need to turn the references back into unnamed references with std::forward. So, t1 in outer is always an lvalue expression while forward<T1>(t1) may be an rvalue expression depending on T1. The latter is only an lvalue expression if T1 is an lvalue reference. And T1 is only deduced to be an lvalue reference in case the first argument to outer was an lvalue expression.
How would that affect the called function inner if we leave t1 & t2 as lvalue?
If, after instantiating, T1 is of type char, and T2 is of a class, you want to pass t1 per copy and t2 per const reference. Well, unless inner() takes them per non-const reference, that is, in which case you want to do so, too.
Try to write a set of outer() functions which implement this without rvalue references, deducing the right way to pass the arguments from inner()'s type. I think you'll need something 2^2 of them, pretty hefty template-meta stuff to deduce the arguments, and a lot of time to get this right for all cases.
And then someone comes along with an inner() that takes arguments per pointer. I think that now makes 3^2. (Or 4^2. Hell, I can't be bothered to try to think whether const pointer would make a difference.)
And then imagine you want to do this for a five parameters. Or seven.
Now you know why some bright minds came up with "perfect forwarding": It makes the compiler do all this for you.
A point that hasn't been made crystal clear is that static_cast<T&&> handles const T& properly too.
Program:
#include <iostream>
using namespace std;
void g(const int&)
{
cout << "const int&\n";
}
void g(int&)
{
cout << "int&\n";
}
void g(int&&)
{
cout << "int&&\n";
}
template <typename T>
void f(T&& a)
{
g(static_cast<T&&>(a));
}
int main()
{
cout << "f(1)\n";
f(1);
int a = 2;
cout << "f(a)\n";
f(a);
const int b = 3;
cout << "f(const b)\n";
f(b);
cout << "f(a * b)\n";
f(a * b);
}
Produces:
f(1)
int&&
f(a)
int&
f(const b)
const int&
f(a * b)
int&&
Note that 'f' has to be a template function. If it's just defined as 'void f(int&& a)' this doesn't work.
It may be worthwhile to emphasize that forward has to be used in tandem with an outer method with forwarding/universal reference. Using forward by itself as the following statements is allowed, but does no good other than causing confusion. The standard committee may want to disable such flexibility otherwise why don't we just use static_cast instead?
std::forward<int>(1);
std::forward<std::string>("Hello");
In my opinion, move and forward are design patterns which are natural outcomes after r-value reference type is introduced. We should not name a method assuming it is correctly used unless incorrect usage is forbidden.
From another viewpoint, when dealing with rvalues in a universal reference assignment, it may be desirable to preserve the type of a variable as it is. For example
auto&& x = 2; // x is int&&
auto&& y = x; // But y is int&
auto&& z = std::forward<decltype(x)>(x); // z is int&&
Using std::forward, we ensured z exactly has the same type as x.
Moreover, std::forward doesn't affect lvalue references:
int i;
auto&& x = i; // x is int&
auto&& y = x; // y is int&
auto&& z = std::forward<decltype(x)>(x); // z is int&
Still z has the same type as x.
So, back to your case, if the inner function has two overloads for int& and int&&, you want to pass variables like z assignment not y one.
The types in the example can be assessed via:
std::cout<<is_same_v<int&,decltype(z)>;
std::cout<<is_same_v<int&&,decltype(z)>;

What are the main purposes of std::forward and which problems does it solve?

In perfect forwarding, std::forward is used to convert the named rvalue references t1 and t2 to unnamed rvalue references. What is the purpose of doing that? How would that affect the called function inner if we leave t1 & t2 as lvalues?
template <typename T1, typename T2>
void outer(T1&& t1, T2&& t2)
{
inner(std::forward<T1>(t1), std::forward<T2>(t2));
}
You have to understand the forwarding problem. You can read the entire problem in detail, but I'll summarize.
Basically, given the expression E(a, b, ... , c), we want the expression f(a, b, ... , c) to be equivalent. In C++03, this is impossible. There are many attempts, but they all fail in some regard.
The simplest is to use an lvalue-reference:
template <typename A, typename B, typename C>
void f(A& a, B& b, C& c)
{
E(a, b, c);
}
But this fails to handle temporary values (rvalues): f(1, 2, 3);, as those cannot be bound to an lvalue-reference.
The next attempt might be:
template <typename A, typename B, typename C>
void f(const A& a, const B& b, const C& c)
{
E(a, b, c);
}
Which fixes the above problem because "const X& binds to everything", including both lvalues and rvalues, but this causes a new problem. It now fails to allow E to have non-const arguments:
int i = 1, j = 2, k = 3;
void E(int&, int&, int&);
f(i, j, k); // oops! E cannot modify these
The third attempt accepts const-references, but then const_cast's the const away:
template <typename A, typename B, typename C>
void f(const A& a, const B& b, const C& c)
{
E(const_cast<A&>(a), const_cast<B&>(b), const_cast<C&>(c));
}
This accepts all values, can pass on all values, but potentially leads to undefined behavior:
const int i = 1, j = 2, k = 3;
E(int&, int&, int&);
f(i, j, k); // ouch! E can modify a const object!
A final solution handles everything correctly...at the cost of being impossible to maintain. You provide overloads of f, with all combinations of const and non-const:
template <typename A, typename B, typename C>
void f(A& a, B& b, C& c);
template <typename A, typename B, typename C>
void f(const A& a, B& b, C& c);
template <typename A, typename B, typename C>
void f(A& a, const B& b, C& c);
template <typename A, typename B, typename C>
void f(A& a, B& b, const C& c);
template <typename A, typename B, typename C>
void f(const A& a, const B& b, C& c);
template <typename A, typename B, typename C>
void f(const A& a, B& b, const C& c);
template <typename A, typename B, typename C>
void f(A& a, const B& b, const C& c);
template <typename A, typename B, typename C>
void f(const A& a, const B& b, const C& c);
N arguments require 2N combinations, a nightmare. We'd like to do this automatically.
(This is effectively what we get the compiler to do for us in C++11.)
In C++11, we get a chance to fix this. One solution modifies template deduction rules on existing types, but this potentially breaks a great deal of code. So we have to find another way.
The solution is to instead use the newly added rvalue-references; we can introduce new rules when deducing rvalue-reference types and create any desired result. After all, we cannot possibly break code now.
If given a reference to a reference (note reference is an encompassing term meaning both T& and T&&), we use the following rule to figure out the resulting type:
"[given] a type TR that is a reference to a type T, an attempt to create the type “lvalue reference to cv TR” creates the type “lvalue reference to T”, while an attempt to create the type “rvalue reference to cv TR” creates the type TR."
Or in tabular form:
TR R
T& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T& && -> T& // rvalue reference to cv TR -> TR (lvalue reference to T)
T&& & -> T& // lvalue reference to cv TR -> lvalue reference to T
T&& && -> T&& // rvalue reference to cv TR -> TR (rvalue reference to T)
Next, with template argument deduction: if an argument is an lvalue A, we supply the template argument with an lvalue reference to A. Otherwise, we deduce normally. This gives so-called universal references (the term forwarding reference is now the official one).
Why is this useful? Because combined we maintain the ability to keep track of the value category of a type: if it was an lvalue, we have an lvalue-reference parameter, otherwise we have an rvalue-reference parameter.
In code:
template <typename T>
void deduce(T&& x);
int i;
deduce(i); // deduce<int&>(int& &&) -> deduce<int&>(int&)
deduce(1); // deduce<int>(int&&)
The last thing is to "forward" the value category of the variable. Keep in mind, once inside the function the parameter could be passed as an lvalue to anything:
void foo(int&);
template <typename T>
void deduce(T&& x)
{
foo(x); // fine, foo can refer to x
}
deduce(1); // okay, foo operates on x which has a value of 1
That's no good. E needs to get the same kind of value-category that we got! The solution is this:
static_cast<T&&>(x);
What does this do? Consider we're inside the deduce function, and we've been passed an lvalue. This means T is a A&, and so the target type for the static cast is A& &&, or just A&. Since x is already an A&, we do nothing and are left with an lvalue reference.
When we've been passed an rvalue, T is A, so the target type for the static cast is A&&. The cast results in an rvalue expression, which can no longer be passed to an lvalue reference. We've maintained the value category of the parameter.
Putting these together gives us "perfect forwarding":
template <typename A>
void f(A&& a)
{
E(static_cast<A&&>(a));
}
When f receives an lvalue, E gets an lvalue. When f receives an rvalue, E gets an rvalue. Perfect.
And of course, we want to get rid of the ugly. static_cast<T&&> is cryptic and weird to remember; let's instead make a utility function called forward, which does the same thing:
std::forward<A>(a);
// is the same as
static_cast<A&&>(a);
I think to have a conceptual code implementing std::forward can help with the understanding. This is a slide from Scott Meyers talk An Effective C++11/14 Sampler
Function move in the code is std::move. There is a (working) implementation for it earlier in that talk. I found actual implementation of std::forward in libstdc++, in file move.h, but it is not at all instructive.
From a user's perspective, the meaning of it is that std::forward is a conditional cast to an rvalue. It can be useful if I am writing a function which expects either an lvalue or rvalue in a parameter and wants to pass it to another function as an rvalue only if it was passed in as an rvalue. If I did not wrap the parameter in std::forward, it would be always passed as a normal reference.
#include <iostream>
#include <string>
#include <utility>
void overloaded_function(std::string& param) {
std::cout << "std::string& version" << std::endl;
}
void overloaded_function(std::string&& param) {
std::cout << "std::string&& version" << std::endl;
}
template<typename T>
void pass_through(T&& param) {
overloaded_function(std::forward<T>(param));
}
int main() {
std::string pes;
pass_through(pes);
pass_through(std::move(pes));
}
Sure enough, it prints
std::string& version
std::string&& version
The code is based on an example from the previously mentioned talk. Slide 10, at about 15:00 from the start.
In perfect forwarding, std::forward is used to convert the named rvalue reference t1 and t2 to unnamed rvalue reference. What is the purpose of doing that? How would that effect the called function inner if we leave t1 & t2 as lvalue?
template <typename T1, typename T2> void outer(T1&& t1, T2&& t2)
{
inner(std::forward<T1>(t1), std::forward<T2>(t2));
}
If you use a named rvalue reference in an expression it is actually an lvalue (because you refer to the object by name). Consider the following example:
void inner(int &, int &); // #1
void inner(int &&, int &&); // #2
Now, if we call outer like this
outer(17,29);
we would like 17 and 29 to be forwarded to #2 because 17 and 29 are integer literals and as such rvalues. But since t1 and t2 in the expression inner(t1,t2); are lvalues, you'd be invoking #1 instead of #2. That's why we need to turn the references back into unnamed references with std::forward. So, t1 in outer is always an lvalue expression while forward<T1>(t1) may be an rvalue expression depending on T1. The latter is only an lvalue expression if T1 is an lvalue reference. And T1 is only deduced to be an lvalue reference in case the first argument to outer was an lvalue expression.
How would that affect the called function inner if we leave t1 & t2 as lvalue?
If, after instantiating, T1 is of type char, and T2 is of a class, you want to pass t1 per copy and t2 per const reference. Well, unless inner() takes them per non-const reference, that is, in which case you want to do so, too.
Try to write a set of outer() functions which implement this without rvalue references, deducing the right way to pass the arguments from inner()'s type. I think you'll need something 2^2 of them, pretty hefty template-meta stuff to deduce the arguments, and a lot of time to get this right for all cases.
And then someone comes along with an inner() that takes arguments per pointer. I think that now makes 3^2. (Or 4^2. Hell, I can't be bothered to try to think whether const pointer would make a difference.)
And then imagine you want to do this for a five parameters. Or seven.
Now you know why some bright minds came up with "perfect forwarding": It makes the compiler do all this for you.
A point that hasn't been made crystal clear is that static_cast<T&&> handles const T& properly too.
Program:
#include <iostream>
using namespace std;
void g(const int&)
{
cout << "const int&\n";
}
void g(int&)
{
cout << "int&\n";
}
void g(int&&)
{
cout << "int&&\n";
}
template <typename T>
void f(T&& a)
{
g(static_cast<T&&>(a));
}
int main()
{
cout << "f(1)\n";
f(1);
int a = 2;
cout << "f(a)\n";
f(a);
const int b = 3;
cout << "f(const b)\n";
f(b);
cout << "f(a * b)\n";
f(a * b);
}
Produces:
f(1)
int&&
f(a)
int&
f(const b)
const int&
f(a * b)
int&&
Note that 'f' has to be a template function. If it's just defined as 'void f(int&& a)' this doesn't work.
It may be worthwhile to emphasize that forward has to be used in tandem with an outer method with forwarding/universal reference. Using forward by itself as the following statements is allowed, but does no good other than causing confusion. The standard committee may want to disable such flexibility otherwise why don't we just use static_cast instead?
std::forward<int>(1);
std::forward<std::string>("Hello");
In my opinion, move and forward are design patterns which are natural outcomes after r-value reference type is introduced. We should not name a method assuming it is correctly used unless incorrect usage is forbidden.
From another viewpoint, when dealing with rvalues in a universal reference assignment, it may be desirable to preserve the type of a variable as it is. For example
auto&& x = 2; // x is int&&
auto&& y = x; // But y is int&
auto&& z = std::forward<decltype(x)>(x); // z is int&&
Using std::forward, we ensured z exactly has the same type as x.
Moreover, std::forward doesn't affect lvalue references:
int i;
auto&& x = i; // x is int&
auto&& y = x; // y is int&
auto&& z = std::forward<decltype(x)>(x); // z is int&
Still z has the same type as x.
So, back to your case, if the inner function has two overloads for int& and int&&, you want to pass variables like z assignment not y one.
The types in the example can be assessed via:
std::cout<<is_same_v<int&,decltype(z)>;
std::cout<<is_same_v<int&&,decltype(z)>;