_Remove_reference exists for, as you know, converting T& to T or T&& to T.
I made the following code in a playful mood, it doesn't work at all as I expected, but have no idea why.
template<class _Ty>
struct _Remove_reference
{ // remove reference
typedef _Ty _Type;
static void func(){ cout << "1" << endl; }
};
// template<class _Ty>
// struct _Remove_reference<_Ty&>
// { // remove reference
// typedef _Ty _Type;
// static void func(){ cout << "2" << endl; }
// };
//
// template<class _Ty>
// struct _Remove_reference<_Ty&&>
// { // remove rvalue reference
// typedef _Ty _Type;
// static void func(){ cout << "3" << endl; }
// };
template<class _Ty> inline
typename _Remove_reference<_Ty>::_Type&&
move(_Ty&& _Arg)
{ // forward _Arg as movable
typename _Remove_reference<_Ty>::func();
return ((typename _Remove_reference<_Ty>::_Type&&)_Arg);
}
int main(){
int a1= 3;
int&& a2 = move(a1); // can't convert the a1 to int&&
return 0;
}
I guess it's all about reference collapsing rules, and template argument deduction but I am confused. my curiosity about this has to be shattered for me to sleep tight.
Thanks in advance.
Given
template<class _Ty> inline
typename _Remove_reference<_Ty>::_Type&&
move(_Ty&& _Arg)
When you do move(a1), _Ty is deduced as int&. _Ty&& would thus still be int& due to the reference collapsing rules, so you need to remove the reference to get int before you can make it an rvalue reference.
This is a special case of the template argument deduction rules. If you have a function argument which is T&& where T is a template parameter, then if you pass an lvalue (i.e. a named object or an lvalue reference) to the function then T is deduced as X& where X is the type of the lvalue object. If you pass an rvalue (a temporary or an rvalue reference) to the function then T is just deduced as X.
e.g. move(a1) deduces _Ty to be int&, whereas move(42) deduces _Ty to be int.
BTW: I guess you took this code from your compiler's standard library --- names decorated with a leading underscore and a capital letter _Like _This are reserved for the compiler and standard library implementation.
Related
#include <iostream>
using namespace std;
namespace mine {
template <typename T>
struct remove_rval {
using type = T;
};
template <typename T>
struct remove_rval<T&&> {
using type = T;
};
template <typename T>
void g(const T& = typename remove_rval<T>::type())
cout << __PRETTY_FUNCTION__ << endl;
}
}
int main()
{
mine::g<int&&>(); // doesn't work, because of explicit template?
const int& i2 = mine::remove_rval<int&&>::type(); // works, sanity check
return 0;
}
The function template I wrote fails to compile. From my understanding of c++, you can assign an rvalue to a constant lvalue reference. But, in this situation, it is like the deduced type disregards the 'const' qualifier when assigning the function default value. Why is this?
From dcl.ref/p6:
If a typedef-name ([dcl.typedef], [temp.param]) or a decltype-specifier ([dcl.type.decltype]) denotes 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.
Thus in your example, when T = int&& :
const T& collapses to T&(which isint&) and not const T&(which is const int&) according to the above quoted statement. And since we can't bind an rvalue like remove_rval<T>::type() to a non-const lvalue reference, you get the mentioned error.
Thus, even though the form of the function parameter in g is a reference to const T aka const lvalue reference(i.e., const T&), the first call to g instantiates g with a reference to non-const T aka non-const lvalue reference(i.e., T& =int&) as the parameter:
template<>
void g<int &&>(int&)
{
//operator<< called here
}
And since the parameter is int&, we cannot bind an rvalue like remove_rval<int&&>::type() to that parameter.
void g(const T& = typename remove_rval<T>::type())
Reference collapsing rules make const T& equal to U& when T = U&&. Lvalue-references can't bind to temporaries like remove_rval<T>::type(). You can instead simply pass int as a template argument and the parameter's type will correctly be const int&.
This question already has answers here:
What are the main purposes of std::forward and which problems does it solve?
(7 answers)
Closed 6 years ago.
In a function template like this
template <typename T>
void foo(T&& x) {
bar(std::forward<T>(x));
}
Isn't x an rvalue reference inside foo, if foo is called with an rvalue reference? If foo is called with an lvalue reference, the cast isn't necessary anyway, because x will also be an lvalue reference inside of foo. Also T will be deduced to the lvalue reference type, and so std::forward<T> won't change the type of x.
I conducted a test using boost::typeindex and I get exactly the same types with and without std::forward<T>.
#include <iostream>
#include <utility>
#include <boost/type_index.hpp>
using std::cout;
using std::endl;
template <typename T> struct __ { };
template <typename T> struct prt_type { };
template <typename T>
std::ostream& operator<<(std::ostream& os, prt_type<T>) {
os << "\033[1;35m" << boost::typeindex::type_id<T>().pretty_name()
<< "\033[0m";
return os;
}
template <typename T>
void foo(T&& x) {
cout << prt_type<__<T>>{} << endl;
cout << prt_type<__<decltype(x)>>{} << endl;
cout << prt_type<__<decltype(std::forward<T>(x))>>{} << endl;
cout << endl;
}
int main(int argc, char* argv[])
{
foo(1);
int i = 2;
foo (i);
const int j = 3;
foo(j);
foo(std::move(i));
return 0;
}
The output of g++ -Wall test.cc && ./a.out with gcc 6.2.0 and boost 1.62.0 is
__<int>
__<int&&>
__<int&&>
__<int&>
__<int&>
__<int&>
__<int const&>
__<int const&>
__<int const&>
__<int>
__<int&&>
__<int&&>
Edit: I found this answer: https://stackoverflow.com/a/27409428/2640636 Apparently,
as soon as you give a name to the parameter it is an lvalue.
My question is then, why was this behavior chosen over keeping rvalue references as rvalues even when they are given names? It seems to me that the whole forwarding ordeal could be circumvented that way.
Edit2: I'm not asking about what std::forward does. I'm asking about why it's needed.
Isn't x an rvalue reference inside foo ?
No, x is a lvalue inside foo (it has a name and an address) of type rvalue reference. Combine that with reference collapsing rules and template type deduction rules and you'll see that you need std::forward to get the right reference type.
Basically, if what you pass to as x is a lvalue, say an int, then T is deduced as int&. Then int && & becomes int& (due to reference collapsing rules), i.e. a lvalue ref.
On the other hand, if you pass a rvalue, say 42, then T is deduced as int, so at the end you have an int&& as the type of x, i.e. a rvalue. Basically that's what std::forward does: casts to T&& the result, like a
static_cast<T&&>(x)
which becomes either T&& or T& due reference collapsing rules.
Its usefulness becomes obvious in generic code, where you may not know in advance whether you'll get a rvalue or lvalue. If you don't invoke std::forward and only do f(x), then x will always be a lvalue, so you'll be losing move semantics when needed and may end up with un-necessary copies etc.
Simple example where you can see the difference:
#include <iostream>
struct X
{
X() = default;
X(X&&) {std::cout << "Moving...\n";};
X(const X&) {std::cout << "Copying...\n";}
};
template <typename T>
void f1(T&& x)
{
g(std::forward<T>(x));
}
template <typename T>
void f2(T&& x)
{
g(x);
}
template <typename T>
void g(T x)
{ }
int main()
{
X x;
std::cout << "with std::forward\n";
f1(X{}); // moving
std::cout << "without std::forward\n";
f2(X{}); // copying
}
Live on Coliru
You really don't want your parameters to be automatically moved to the functions called. Consider this function:
template <typename T>
void foo(T&& x) {
bar(x);
baz(x);
global::y = std::forward<T>(x);
}
Now you really don't want an automatic move to bar and an empty parameter to baz.
The current rules of requiring you to specify if and when to move or forward a parameter are not accidental.
I get exactly the same types with and without std::forward<T>
...no? Your own output proves you wrong:
__<int> // T
__<int&&> // decltype(x)
__<int&&> // std::forward<T>(x)
Without using std::forward<T> or decltype(x) you will get int instead of int&&. This may inadvertently fail to "propagate the rvalueness" of x - consider this example:
void foo(int&) { cout << "int&\n"; }
void foo(int&&) { cout << "int&&\n"; }
template <typename T>
void without_forward(T&& x)
{
foo(x);
// ^
// `x` is an lvalue!
}
template <typename T>
void with_forward(T&& x)
{
// `std::forward` casts `x` to `int&&`.
// vvvvvvvvvvvvvvvvvv
foo(std::forward<T>(x));
// ^
// `x` is an lvalue!
}
template <typename T>
void with_decltype_cast(T&& x)
{
// `decltype(x)` is `int&&`. `x` is casted to `int&&`.
// vvvvvvvvvvv
foo(decltype(x)(x));
// ^
// `x` is an lvalue!
}
int main()
{
without_forward(1); // prints "int&"
with_forward(1); // prints "int&&"
with_decltype_cast(1); // prints "int&&"
}
wandbox example
x being an r-value is NOT the same thing as x having an r-value-reference type.
R-value is a property of an expression, whereas r-value-reference is a property of its type.
If you actually try to pass a variable that is an r-value reference to a function, it is treated like an l-value. The decltype is misleading you. Try it and see:
#include <iostream>
#include <typeinfo>
using namespace std;
template<class T> struct wrap { };
template<class T>
void bar(T &&value) { std::cout << " vs. " << typeid(wrap<T>).name() << std::endl; }
template<class T>
void foo(T &&value) { std::cout << typeid(wrap<T>).name(); return bar(value); }
int main()
{
int i = 1;
foo(static_cast<int &>(i));
foo(static_cast<int const &>(i));
foo(static_cast<int &&>(i));
foo(static_cast<int const &&>(i));
}
Output:
4wrapIRiE vs. 4wrapIRiE
4wrapIRKiE vs. 4wrapIRKiE
4wrapIiE vs. 4wrapIRiE (these should match!)
4wrapIKiE vs. 4wrapIRKiE (these should match!)
Can someone help me to understand why the output of the following code
template< typename T >
void check()
{
std::cout << "unknow type" << std::endl;
}
template<>
void check<int>()
{
std::cout << "int" << std::endl;
}
template<>
void check<int&>()
{
std::cout << "int&" << std::endl;
}
template<>
void check<int&&>()
{
std::cout << "int&&" << std::endl;
}
template< typename T >
void bar( T&& a )
{
check<T>();
}
int main()
{
bar(0);
int a = 0;
bar( a );
}
is
int
int&
and not
int&&
int&
From my point of view, it seems more intuitive that an r-value reference remains as an r-value reference and an l-value reference an l-value reference, However, it seems that only l-value references remains as l-value references and r-values become non-reference values.
What is the motivation/idea behind this?
bar(0); calls the specialization bar<int>(int&&) i.e. T is deduced as int, so check<T>() is check<int>(). The parameter type is T&& which is int&&, but that's the type of the parameter, not the type T.
This is entirely consistent with non-forwarding references. If you define:
template<typename T> void baz(T&);
and you call it with an lvalue of type int then T is deduced as int, not int&
The only thing that's special about forwarding references like your example uses is that for T&& the type can be deduced as an lvalue reference, call it R, in which case the parameter type is R&& which is the same as add_rvalue_reference_t<R> which is just R. So for the call bar(a) you call the specialization bar<int&>(int&) i.e. T is deduced as int&
When you call bar<int&&>(0) with an explicit template argument list there is no argument deduction, and so T is substituted by int&&, so the parameter type T&& is add_rvalue_reference_t<int&&> which is just int&&.
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.
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