How do two overloaded std::forward work? [duplicate] - c++

This question already has an answer here:
Why does std::forward have two overloads?
(1 answer)
Closed 5 years ago.
I have found in std library the following implementation of std::forward:
// TEMPLATE FUNCTION forward
template<class _Ty> inline
constexpr _Ty&& forward(
typename remove_reference<_Ty>::type& _Arg) _NOEXCEPT
{ // forward an lvalue as either an lvalue or an rvalue
return (static_cast<_Ty&&>(_Arg));
}
template<class _Ty> inline
constexpr _Ty&& forward(
typename remove_reference<_Ty>::type&& _Arg) _NOEXCEPT
{ // forward an rvalue as an rvalue
static_assert(!is_lvalue_reference<_Ty>::value, "bad forward call");
return (static_cast<_Ty&&>(_Arg));
}
First function works obviously, but for second I cannot find usefull example.
If I try to do something like this:
template<class _Ty> inline
constexpr _Ty&& my_forward(
typename std::remove_reference<_Ty>::type&& _Arg) _NOEXCEPT
{ // forward an rvalue as an rvalue
static_assert(!std::is_lvalue_reference<_Ty>::value, "bad forward call");
return (static_cast<_Ty&&>(_Arg));
}
template<typename T>
T getRValue()
{
return std::remove_reference<T>::type{};
}
template<typename T>
void setValue(T && i)
{
my_forward<T>(getRValue<T>());
}
int main()
{
int i = 1;
setValue(i);
}
to check when second function is called, I have got the error:
Error C2664 '_Ty my_forward<T>(int &&) noexcept': cannot convert argument 1 from 'int' to 'int &&'
Does anybody know in which case second overloaded function (in my terms my_forward) is called ?
Or may be some good example for it ?
By the way my compiler is MSVC 2015
Thanks, all for help !!

int bad_forward_call() {
return std::forward<int&>(7);
}
this will invoke that version.
Note I'm forwarding an rvalue as an lvalue. Not allowed.
Your getRValue fails to get an rvalue if T is a reference type.

I have found cases in which this function is called:
my_forward<int&>(8); // This calls forward(typename remove_reference<_Ty>::type&& _Arg)
But it won't compile 'cause we try convert rvalue to lvalue
my_forward<int&&>(8); // This calls forward(typename remove_reference<_Ty>::type&& _Arg)
But it compiles successfully
If it may be useful for somebody, the purpose of std::forward is to forward operation to another class or function.
Consider the following example:
template<typename T>
void precompute(T & t)
{
std::cout << "lvalue reference";
}
template<typename T,
std::enable_if_t<!std::is_reference<T>::value, bool> = true>
void precompute(T && t)
{
std::cout << "rvalue reference";
}
template<typename T>
void calculate(T && t)
{
precompute(t); // Every time called precompute(T & t)
}
or like this:
template<typename T>
void precompute(T & t)
{
std::cout << "lvalue reference";
}
template<typename T,
std::enable_if_t<!std::is_reference<T>::value, bool> = true>
void precompute(T && t)
{
std::cout << "rvalue reference";
}
template<typename T>
void calculate(T && t)
{
precompute(std::move(t)); // Every time called precompute(T && t)
}
As you can see we have a problem !! Neither of two examples satisfied us.
In first example every time will be called lvalue function and nothing can be done to call first with rvalue.
In second example every time will be called rvalue function and nothing can be done to call first with lvalue.
The solution is to forward making decision to the called function:
template<typename T>
void precompute(T & t)
{
std::cout << "lvalue reference";
}
template<typename T,
std::enable_if_t<!std::is_reference<T>::value, bool> = true>
void precompute(T && t)
{
std::cout << "rvalue reference";
}
template<typename T>
void calculate(T && t)
{
precompute(std::forward<T>(t)); // Will be called either precompute(T & t) or precompute(T && t) depends on type of t
}
In this case we have to be sure that will be called appropriate version.
That is why we forward (means: "Please, check type of t and if it is rvalue -> call rvalue version of function, and if it is lvalue -> call lvalue version of function") this operation to called function.

Related

Why template argument cannot be deduced in this context?

Could anyone explain why compilers (g++, visual c++) fail to deduce the template argument in this case?
struct MyClass
{
void Foo(int x)& {}
void Foo(int x)&& {}
};
template<typename T>
void CallFoo(void(T::*func)(int)&)
{
//create instance and call func
}
int main()
{
CallFoo(&MyClass::Foo); // Fails to deduce T
}
Why compilers cannot deduce T to MyClass? This happens only for methods overloaded by ref qualifiers. If a method is overloaded by const-ness or parameter types, everything works fine.
It seems that only Clang can deduce T in this case.
Summarizing discussion in the comments:
the support for reference-qualified member functions as template arguments is a relatively new feature for some compilers. However, the latest versions of most compilers will compile such code.
For example:
#include <iostream>
struct MyClass
{
void Foo(int) const &
{
std::cout << "calling: void Foo(int) const &\n";
}
void Foo(int) const &&
{
std::cout << "calling: void Foo(int) const &&\n";
}
};
template<typename T>
void CallFoo_lvalue(void (T::*foo)(int) const &)
{
T temp;
(temp.*foo)(0);
}
template<typename T>
void CallFoo_rvalue(void (T::*foo)(int) const &&)
{
(T{}.*foo)(0);
}
int main()
{
CallFoo_lvalue(&MyClass::Foo);
CallFoo_rvalue(&MyClass::Foo);
}
Will compile with:
gcc (works from 7.0.0)
Visual C++ (works with v19.10.24903.0)
producing the following output:
calling: void Foo(int) const &
calling: void Foo(int) const &&
For those who are wondering what & and && are for: here's the quote from #JustinTime:
Basically, & is the lvalue ref-qualifier, and && is the rvalue
ref-qualifier (binds to temporary object); in his example, MyClass m;
m.Foo(3); would call the top one, while MyClass{}.Foo(3); would call
the bottom one. They act on the implicit object parameter; lvalue
ref-qualifier binds to lvalue reference, and rvalue ref-qualifier
binds to rvalue reference (functions that have neither take the
parameter as lvalue reference, but let it bind to either). Note that
they don't actually change *this's type.
If you want your template to bind to different reference types, you need to use the universal reference
template<typename T>
void func(T&& arg)
{
other_func(std::forward<T>(arg));
}
That will bind to either lvalue or rvalue references. std::forward will make sure the appropriate reference is used in subsequent calls. I'm not sure how to fit the double ampersand into your code, but maybe just
template<typename T>
void CallFoo(void(T::*func)(int)&&)
Perhaps better would be
template<typename func_t>
void CallFoo(func_t && f)
{
call(std::forward<func_t>(f));
}
template<typename func_t>
void call(typename std::remove_reference<func_t> & f)
{
f();
}
template<typename func_t>
void call(typename std::remove_reference<func_t> && f)
{
f();
}
or whatever syntax you need to invoke a function pointer, maybe *f();
And if you want to pass arguments as well:
template<typename func_t, typename ... args_t>
void CallFoo(func_t && f, args_t && ... args)
{
call(std::forward<func_t>(f), std::forward<args_t>(args)...);
}
template<typename func_t, typename ... args_t>
void call(typename std::remove_reference<func_t> & f, args_t && ... args)
{
f(std::forward<args_t>(args)...);
}
template<typename func_t, typename ... args_t>
void call(typename std::remove_reference<func_t> && f, args_t && ... args)
{
f(std::forward<args_t>(args)...);
}

When do template parameters resolve to rvalue references to lvalue references, or the other way around?

I was reading about rvalue references and perfect forwarding when I came across this article on MSDN: https://msdn.microsoft.com/en-us/library/dd293668.aspx
My question is about this example from the article:
#include <iostream>
#include <string>
using namespace std;
template<typename T> struct S;
// The following structures specialize S by
// lvalue reference (T&), const lvalue reference (const T&),
// rvalue reference (T&&), and const rvalue reference (const T&&).
// Each structure provides a print method that prints the type of
// the structure and its parameter.
template<typename T> struct S<T&> {
static void print(T& t)
{
cout << "print<T&>: " << t << endl;
}
};
template<typename T> struct S<const T&> {
static void print(const T& t)
{
cout << "print<const T&>: " << t << endl;
}
};
template<typename T> struct S<T&&> {
static void print(T&& t)
{
cout << "print<T&&>: " << t << endl;
}
};
template<typename T> struct S<const T&&> {
static void print(const T&& t)
{
cout << "print<const T&&>: " << t << endl;
}
};
// This function forwards its parameter to a specialized
// version of the S type.
template <typename T> void print_type_and_value(T&& t)
{
S<T&&>::print(std::forward<T>(t));
}
// This function returns the constant string "fourth".
const string fourth() { return string("fourth"); }
int main()
{
// The following call resolves to:
// print_type_and_value<string&>(string& && t)
// Which collapses to:
// print_type_and_value<string&>(string& t)
string s1("first");
print_type_and_value(s1);
// The following call resolves to:
// print_type_and_value<const string&>(const string& && t)
// Which collapses to:
// print_type_and_value<const string&>(const string& t)
const string s2("second");
print_type_and_value(s2);
// The following call resolves to:
// print_type_and_value<string&&>(string&& t)
print_type_and_value(string("third"));
// The following call resolves to:
// print_type_and_value<const string&&>(const string&& t)
print_type_and_value(fourth());
}
My question is, why does this call:
print_type_and_value(s1);
resolve to:
print_type_and_value<string&>(string& &&t)
If my understanding is correct, string& && is an rvalue reference to an lvalue reference. Why is this? The variable s1 is an lvalue (it is not temporary, it is addressable, and it can be accessed from multiple parts of the program), so shouldn't the call resolve to string& (a simple lvalue reference)? I don't see where the double reference came from. s1 is a value, not a reference, isn't it? Why does this call involve rvalues at all?
In more general terms, I am a bit confused as to when template parameters resolve to T& && (an rvalue reference to an lvalue reference?) or T&& & (an lvalue reference to an rvalue reference?).
So, could someone please explain the following:
Why did the call to print_type_and_value(s1) resolve to print_type_and_value(string& &&t) ?
In general, when does f(var) resolve to f(T& &&x) or f(T&& &x) ?
I've seen examples in which template parameters resolve to T&& &&, which looks to me like an rvalue reference to an rvalue reference. When does this happen?
Of course, I am aware of the reference collapsing rules, and I understand that T& & is collapsed to T&, but I'm wondering why the call in this example resolved to T& && in the first place.
Thanks in advance for your help!
Edit:
I understand the basics of reference collapsing, but one thing that I'd like to know is why this specific example behaved in the way it did.
Why did print_type_and_value(s1) resolve to print_type_and_value(string& &&t) and then collapse to print_type_and_value(string& t) ?
Edit 2:
Thanks a lot for your links! I'm starting to understand it.
I just have one more question. Why does the template type evaluate to string& when a variable of type string is passed?
Edit 3:
I've re-read the links you've posted, and I 100% get It now. Thanks again!
The reference collapsing rule make print_type_and_value<string&>(string& &&t) equivalent to print_type_and_value<string&>(string& t): there are no reference to reference.
Here is an excellent question/answer on SO regarding this rule.

In std::forward how does it accept rvalue?

Looking at Scott Meyer's Effective Modern C++ pages 200-201, the suggested simplified implementation of std::forward could be (did see the proper implementation elsewhere):
template <typename T>
T&& forward(std::remove_reference_t<T>& param)
{ return static_cast<T&&>(param); }
And when accepting an rvalue Widget, it becomes:
Widget&& forward(Widget& param)
{ return static_cast<Widget&&>(param); }
Now, if you take that substituted code, and do:
struct Widget { };
Widget&& forward(Widget& param)
{ return static_cast<Widget&&>(param); }
template <typename T>
void G(T&& uref)
{ }
template <typename T>
void F(T&& uref)
{ G(forward(uref)); }
int main()
{
Widget x;
F(std::move(x));
}
What I can't wrap my head around, and didn't see a direct answer on SO yet, is: in forward how does parameter Widget& param manage to accept Widget&& from F()? Normally gcc-5.0 would complain like so with non-template code:
error: invalid initialization of non-const reference of type ‘Widget&’ from an rvalue of type ‘std::remove_reference::type {aka Widget}’
(Question #27501400 nearly touches the topic, but not quite. It shows the standard as having both lvalue & and rvalue && versions.)
"Named rvalue references are lvalues",
so example works fine as noted in a comment.
Nevertheless your code could be modified to
template <typename T>
void F(T && uref)
{
G(forward(move(uref)));
}
which is accepted by another overload (compare):
template<typename T>
T && forward(typename std::remove_reference<T>::type & t)
{
return static_cast<T &&>(t);
}
template<typename T>
T && forward(typename std::remove_reference<T>::type && t)
{
static_assert(!std::is_lvalue_reference<T>::value, "T is an lvalue reference");
return static_cast<T &&>(t);
}
The second overload will be used for rvalue. It works if T is Widget or Widget && and the assert fails for Widget &.

Implement a forward function using universal reference

The implementation of std::forward in VS2013 is
template<class _Ty> inline
_Ty&& forward(typename remove_reference<_Ty>::type& _Arg)
{ // forward an lvalue
return (static_cast<_Ty&&>(_Arg));
}
template<class _Ty> inline
_Ty&& forward(typename remove_reference<_Ty>::type&& _Arg) _NOEXCEPT
{ // forward anything
static_assert(!is_lvalue_reference<_Ty>::value, "bad forward call");
return (static_cast<_Ty&&>(_Arg));
}
One version for lvalue reference, one version for rvalue reference. Why not just use a universal reference for both rvalue and lvalue reference:
template <typename T, typename U>
T&& Forward(U&& arg) {
return static_cast<T&&>(arg);
}
Your version is not standard-compliant, as std::forward is is required to not compile when called with on an rvalue if T is an l-value reference. From [forward]:
template <class T> T&& forward(typename remove_reference<T>::type& t) noexcept;
template <class T> T&& forward(typename remove_reference<T>::type&& t) noexcept;
2 Returns: static_cast<T&&>(t).
3 if the second form is instantiated with an lvalue reference type, the program is ill-formed.
std::forward is defined in this way to ensure that (some) misuses of std::forward do not compile. See n2951 for more discussion (although even n2951 does not use this exact form).
I'm expanding a bit on the problem you've pointed out here.
Your version would introduce a reference-dangling case if you attempt to bind a newly created rvalue to a l-value reference.
As Mankarse linked, the n2951 paper cites this case and, by simplifying it a bit, you can summarize it with the following code
#include <iostream>
using namespace std;
template <typename T, typename U>
T&& Forward(U&& arg) {
return static_cast<T&&>(arg);
}
class Container
{
int data_;
public:
explicit Container(int data = 1) // Set the data variable
: data_(data) {}
~Container() {data_ = -1;} // When destructed, first set the data to -1
void test()
{
if (data_ <= 0)
std::cout << "OPS! A is destructed!\n";
else
std::cout << "A = " << data_ << '\n';
}
};
// This class has a reference to the data object
class Reference_To_Container_Wrapper
{
const Container& a_;
public:
explicit Reference_To_Container_Wrapper(const Container& a) : a_(a) {}
// (I) This line causes problems! This "Container" returned will be destroyed and cause troubles!
const Container get() const {return a_;} // Build a new Container out of the reference and return it
};
template <class T>
struct ReferenceContainer
{
T should_be_valid_lvalue_ref;
template <class U> // U = Reference_To_Container_Wrapper
ReferenceContainer(U&& u) :
// We store a l-value reference to a container, but the container is from line (I)
// and thus will soon get destroyed and we'll have a dangling reference
should_be_valid_lvalue_ref(Forward<T>(std::move(u).get())) {}
};
int main() {
Container a(42); // This lives happily with perfect valid data
ReferenceContainer<const Container&> rc( (Reference_To_Container_Wrapper(a)) ); // Parenthesis necessary otherwise most vexing parse will think this is a function pointer..
// rc now has a dangling reference
Container newContainer = rc.should_be_valid_lvalue_ref; // From reference to Container
newContainer.test();
return 0;
}
which outputs "OPS! A is destructed!"
if you just add a "&" in the line
const Container& get() const {return a_;}
the above works just fine.
http://ideone.com/SyUXss

Preventing non-const lvalues from resolving to rvalue reference instead of const lvalue reference

I'm having trouble overloading a function to take a value either by const reference or, if it is an rvalue, an rvalue reference. The problem is that my non-const lvalues are binding to the rvalue version of the function. I'm doing this in VC2010.
#include <iostream>
#include <vector>
using namespace std;
template <class T>
void foo(const T& t)
{cout << "void foo(const T&)" << endl;}
template <class T>
void foo(T&& t)
{cout << "void foo(T&&)" << endl;}
int main()
{
vector<int> x;
foo(x); // void foo(T&&) ?????
foo(vector<int>()); // void foo(T&&)
}
The priority seems to be to deduce foo(x) as
foo< vector<int> & >(vector<int>& && t)
instead of
foo< vector<int> >(const vector<int>& t)
I tried replacing the rvalue-reference version with
void foo(typename remove_reference<T>::type&& t)
but this only had the effect of causing everything to resolve to the const-lvalue reference version.
How do I prevent this behaviour? And why is this the default anyway - it seems so dangerous given that rvalue-references are allowed to be modified, this leaves me with an unexpectedly modified local variable.
EDIT: Just added non-template versions of the functions, and they work as expected. Making the function a template changes the overload resolution rules? That is .. really frustrating!
void bar(const vector<int>& t)
{cout << "void bar(const vector<int>&)" << endl;}
void bar(vector<int>&& t)
{cout << "void bar(vector<int>&&)" << endl;}
bar(x); // void bar(const vector<int>&)
bar(vector<int>()); // void bar(vector<int>&&)
When you have a templated function like this you almost never want to overload. The T&& parameter is a catch anything parameter. And you can use it to get any behavior you want out of one overload.
#include <iostream>
#include <vector>
using namespace std;
template <class T>
void display()
{
typedef typename remove_reference<T>::type Tr;
typedef typename remove_cv<Tr>::type Trcv;
if (is_const<Tr>::value)
cout << "const ";
if (is_volatile<Tr>::value)
cout << "volatile ";
std::cout << typeid(Trcv).name();
if (is_lvalue_reference<T>::value)
std::cout << '&';
else if (is_rvalue_reference<T>::value)
std::cout << "&&";
std::cout << '\n';
}
template <class T>
void foo(T&& t)
{
display<T>();
}
int main()
{
vector<int> x;
vector<int> const cx;
foo(x); // vector<int>&
foo(vector<int>()); // vector<int>
foo(cx); // const vector<int>&
}
In order for T&& to bind to an lvalue reference, T must itself be an lvalue reference type. You can prohibit the template from being instantiated with a reference type T:
template <typename T>
typename std::enable_if<!std::is_reference<T>::value>::type foo(T&& t)
{
cout << "void foo(T&&)" << endl;
}
enable_if is found in <utility>; is_reference is found in <type_traits>.
The reason that the overload taking T&& is preferred over the overload taking a T const& is that T&& is an exact match (with T = vector<int>&) but T const& requires a qualification conversion (const-qualification must be added).
This only happens with templates. If you have a nontemplate function that takes a std::vector<int>&&, you will only be able to call that function with an rvalue argument. When you have a template that takes a T&&, you should not think of it as "an rvalue reference parameter;" it is a "universal reference parameter" (Scott Meyers used similar language, I believe). It can accept anything.
Allowing a T&& parameter of a function template to bind to any category of argument is what enables perfect forwarding.