std::forward vs std::move while binding lvalue to rvalue reference - c++

is there a difference between move and forward here:
void test(int && val)
{
val=4;
}
void main()
{
int nb;
test(std::forward<int>(nb));
test(std::move(nb));
std::cin.ignore();
}

In your specific case, no, there isn't any difference.
Detailed answer:
Under the hood, std::move(t) does static_cast<typename std::remove_reference<T>::type&&>(t), where T is type of t (see §20.2.3/6). In your case, it resolves to static_cast<int&&>(nb).
forward is a little bit tricky, because it is tailored for use in templates (to allow perfect forwarding) and not as a tool to cast lvalue to rvalue reference.
Standard library provides two overloads (one for lvalue references and the second for rvalue ones, see §20.2.3/2):
template <class T> T&& forward(typename remove_reference<T>::type& t) noexcept;
template <class T> T&& forward(typename remove_reference<T>::type&& t) noexcept;
Substituting int, we get:
int&& forward(int& t) noexcept;
int&& forward(int&& t) noexcept;
And since nb is lvalue, the first version is chosen. According to standard draft, the only effect of forward is static_cast<T&&>(t). With T being int, we get static_cast<int&&>(nb), i.e. - we get two exactly same casts.
Now, if you want to cast lvalue to rvalue (to allow moving), please use only std::move, which is the idiomatic way to do this conversion. std::forward is not intended to be used this way.

No difference.

Related

Is it possible to omit template parameter in `std::forward`?

The standard signature of std::forward is:
template<typename T>
constexpr T&& forward(std::remove_reference_t<T>&) noexcept;
template<typename T>
constexpr T&& forward(std::remove_reference_t<T>&&) noexcept;
Because the parameter type isn't T directly, we should specify the template argument when using std::forward:
template<typename... Args>
void foo(Args&&... args)
{
bar(std::forward<Args>(args)...);
}
However sometimes the template argument is not as simple as Args. auto&& is a case:
auto&& vec = foo();
bar(std::forward<decltype(vec)>(vec));
You can also imagine more complicated template argument types for std::forward. Anyway, intuitively std::forward should know what T is but it actually don't.
So my idea is to omit <Args> and <decltype(vec)> no matter how simple they are. Here is my implementation:
#include <type_traits>
template<typename T>
std::add_rvalue_reference_t<std::enable_if_t<!std::is_lvalue_reference<T>::value, T>>
my_forward(T&& obj)
{
return std::move(obj);
}
template<typename T>
T& my_forward(T& obj)
{
return obj;
}
int main()
{
my_forward(1); // my_forward(int&&)
int i = 2;
my_forward(i); // my_forward(int&)
const int j = 3;
my_forward(j); // my_forward(const int&)
}
When obj is rvalue reference, for example int&&, the first overload is selected because T is int, whose is_lvalue_reference is false;
When obj is lvalue reference, for example const int&, the second overload is selected because T is const int& and the first is SFINAE-ed out.
If my implementation is feasible, why is std::forward still requiring <T>? (So mine must be infeasible.)
If not, what's wrong? And still the question, is it possible to omit template parameter in std::forward?
The problematic case is when you pass something of rvalue reference type but which does not belong to an rvalue value category:
int && ir{::std::move(i)};
my_forward(ir); // my_forward(int&)
Passing type to std::forward will ensure that arguments of rvalue reference types will be moved further as rvalues.
The answer by user7860670 gives you an example for the case where this breaks down. Here is the reason why the explicit template parameter is always needed there.
By looking at the value of the forwarding reference you can no longer reliably determine through overload resolution whether it is safe to move from. When passing an lvalue reference parameter as an argument to a nested function call it will be treated as an lvalue. In particular, it will not bind as an rvalue argument, that would require an explicit std::move again. This curious asymmetry is what breaks implicit forwarding.
The only way to decide whether the argument should be moved onwards is by inspecting its original type. But the called function cannot do so implicitly, which is why we must pass the deduced type explicitly as a template parameter. Only by inspecting that type directly can we determine whether we do or do not want to move for that argument.

Template specialization and perfect forwarding

So I am trying to build a "sanitizer" function to filter formatting arguments before they are forwarded to printf.
template<typename A>
_CR_INLINE decltype(auto) sanitize_forward(A&& arg) {
return std::forward<A>(arg);
}
template<>
_CR_INLINE decltype(auto) sanitize_forward<std::string>(std::string&& arg) {
return std::forward<const char*>(arg.c_str());
}
So every std::string is supposed to "decay" into a const char*, in order to be properly formatted.
template<typename...Args>
_CR_INLINE void printf_safe(const std::string& format, Args&&...args) {
std::printf(format.c_str(), sanitize_forward<Args>(args)...);
}
I want the arguments to be perfectly forwarded to printf, which is why I return std::forward. But I can't really wrap my head around how this should be implemented.
1) Is decltype(auto) correct? It should preserve the r-value referenceness of the return of std::forward, right?
2) How should I specialize my template: std::string&& or std::string?
3) Deduced forwarding references should be the same as the actual type, right?
You almost completely misunderstand forwarding references; which is to be expected, they are confusing.
To understand them, you have to understand reference collapsing rules, std::forward itself, template argument deduction rules when deducing T&&, and how they tie together, at least to some extent.
First, reference collapsing. Look at this chart:
If Then Then
X is X& is X&& is
T T& T&&
T& T& T&
T const& T const& T const&
T&& T& T&&
if X is int, then X&& is int&&. If X is int&, then X&& is int&.
Reread that. Again. One more time. & wins over && when both are applied.
Next, deduction. I will tell lies here, but they are simplifying lies.
If you pass Foo& to a template deducing X&&, X is Foo& and X&& is Foo&.
If you pass Foo&& to a template deducing X&&, X is Foo and X&& is Foo&&.
Next, std::forward is a conditional move . For a reference variable X&& x, std::forward<X>(x) is decltype(x)(x) -- it casts x to the type it was declared as. If x is an rvalue reference, it casts x to an rvalue reference. This is needed because the type of the expression x is X& not X&& even if x is an rvalue reference. An rvalue reference is a reference to an rvalue, but is not itself an rvalue.
Now to fix your code.
template<class T>
struct tag_t{constexpr tag_t(){}};
template<class T>
constexpr tag_t<std::decay_t<T>> tag{};
template<class T>
auto sanitizer(tag_t<T>){
return [](auto&& t)->decltype(auto){
return decltype(t)(t);
};
}
template<class A>
decltype(auto) sanitize(A&& arg) {
return sanitizer(tag<A>)(std::forward<A>(arg));
}
auto sanitizer(tag_t<std::string>) {
return [](std::string const& s){return s.c_str();};
}
template<class...Ts>
void printf_safe(const std::string& format, Ts&&...ts) {
std::printf(format.c_str(), sanitize(std::forward<Ts>(ts))...);
}
I follow SRP (single responsibility principle) -- I split forward from sanitize.
Then I split which sanitizing action (sanitizer) from actually doing it (sanitize).
This let me write the std::string santization code once, without the perfect forwarding winning "by accident".
As an aside, I'd replace decay with remove ref and remove cv if you want to treat array args as non-pointers.
You can extend sanitize functionality by writing sanitizer overloads in either the namespace of tag_t or in the namespace of the type we are sanitizing (bit not in std naturally).
So I am trying to build a "sanitizer" function to filter formatting arguments before they are forwarded to printf.
Your premise seems wrong. printf is a C function which uses va_arg - it doesn't do or need any perfect forwarding.
http://en.cppreference.com/w/cpp/io/c/fprintf
It is also never going to "consume" its arguments, but merely read from them. It doesn't make sense to distinguish temporaries and non-temporaries - just take const Args&... in your printf wrapper.
1) Is decltype(auto) correct? It should preserve the r-value referenceness of the return of std::forward, right?
std::forward will either return an lvalue reference or rvalue reference. decltype(auto) will indeed preserve that.
Your specialization of sanitize_forward for std::string doesn't look useful - the std::forward<const char*> will always return a const char* &&. I don't think it's different from:
template<>
_CR_INLINE const char* sanitize_forward<std::string>(std::string&& arg) {
return arg.c_str();
}
Additionally, returning .c_str() for an rvalue reference to a std::string sounds very dangerous and incorrect: you're taking a pointer to the internal buffer of a string that's about to expire. You probably want to take const std::string& here.
2) How should I specialize my template: std::string&& or std::string?
How is it going to be called? Are you explicitly supplying a template argument? Is the template argument going to always be a non-reference, or is it going to be both an lvalue reference and a non-reference?
Since you have sanitize_forward<Args>, you will probably attempt to invoke both...
sanitize_forward<std::string>
sanitize_forward<std::string&>
...maybe with cv-qualifiers. You might want to supply an additional explicit std::decay_t<Args> parameter that deals with the "specialization" business.
3) Deduced forwarding references should be the same as the actual type, right?
Not sure what you mean by this. Could you elaborate?

Why is T const&& not a forwarding reference?

In the context of a template, the following "reference collapsing" rules are applied:
template <typename T>
void foo(T && t)
{
//T& & -> T&
//T& && -> T&
//T&& & -> T&
//T&& && -> T&&
}
Why does the language prohibit "universal references" from having const qualifiers?
template <typename T>
void foo(T const && t)
It would seem to make sense if the type had resolved to a reference (3 out of the 4 cases).
I'm sure this idea is incompatible with some other design aspect of the language, but I can't quite see the full picture.
Originally the rvalue reference proposal said that the transformation happens if P is "an rvalue reference type". However, a defect report later noticed
Additionally, consider this case:
template <class T> void f(const T&&);
...
int i;
f(i);
If we deduce T as int& in this case then f(i) calls f<int&>(int&), which seems counterintuitive. We prefer that f<int>(const int&&) be called. Therefore, we would like the wording clarified that the A& deduction rule in 14.8.2.1 [temp.deduct.call] paragraph 3 applies only to the form T&& and not to cv T&& as the note currently implies.
There appears to have been a time period where const T &&, with T being U&, was transformed to const U&. That was changed to be consistent with another rule that says that const T, where T is U& would stay U& (cv-qualifiers on references are ignored). So, when you would deduce T in above example to int&, the function parameter would stay int&, not const int&.
In the defect report, the reporter states "We prefer that f<int>(const int&&) be called", however provides no reason in the defect report. I can imagine that the reason was that it seemed too intricate to fix this without introducing inconsistency with other rules, however.
We should also keep in mind that the defect report was made at a time where rvalue references could still bind to lvalues - i.e const int&& could bind to an int lvalue. This was prohibited only later on, when a paper by Dave & Doug, "A Safety Problem with RValue References", appeared. So, it seems to me that a deduction that works (at that time) was worth more than a deduction that simply was counter intuitive and dropped qualifiers.
This does already happen for references; if your T is a U const &, then T && will collapse to U const &. The term "universal reference" really does mean universal reference: you don't need to specify const in there to get a constant reference.
If you want to have a truly universal reference mechanism, you need your T && to be able to become all kinds of references, will all kinds of constness. And, T && does exactly that. It collapses to all four cases: both l- and r-value references, and both const and non-const.
Explained another way, the constness is an attribute of the type, not the reference, i.e. when you say T const &, you are actually talking about a U &, where U is T const. The same is true for && (although an r-value reference to a const is less useful).
This means that if you want your universal reference to collapse to a U const &, just pass it something that is of the type you want: a U const &, and it will collapse to exactly that.
To answer you question more directly: the language does not "prohibit" the use of const in the declaration of a universal reference, per sé. It is saying that if you change the mechanism for declaring a universal reference even a little bit - even by inserting a lowly const between the T and the && - then you won't have a (literally) "universal" reference anymore, because it just won't accept anything and everything.
Why do you think the language does not allow const r-value references?
In the following code, what will be printed?
#include <iostream>
struct Foo
{
void bar() const &
{
std::cout << "&\n";
}
void bar() const &&
{
std::cout << "&&\n";
}
};
const Foo make() {
return Foo{};
}
int main()
{
make().bar();
}
answer:
&&
why? Because make() returns a const object and in this context it's a temporary. Therefore r-value reference to const.
Template argument deduction has a special case for "rvalue reference to cv-unqualified template parameters". It is this very special case that forwarding/universal references rely on. See section "Deduction from a function call" in the linked article for details.
Note that before template argument deduction, all top-level cv-qualifiers are removed; however, references never have top-level cv-qualifiers and above rule does not apply, so the special rule also does not apply. (In contrast to pointers, there is no "const reference", only "reference to const")

Move semantics and perfect forwarding difference

I already got what move semantics is from this question:
What are move semantics?
But I still do not get what perfect forwarding is in relation to move semantics.
Can someone explain in simple english and with a simple example what perfect forwarding means?
Plain English-only attempt
The problem is probably too complex to be accurately described by plain English sentences, but one could think of perfect forwarding as a way to move temporary values passed to a function to another one as if the first function didn't exist at all, so without any unnecessary copies or assignments. C++11 allows you to do this by introducing some conversion rules between r-value (&&) and l-value (&) references to a type when you try to get a reference (either r-value or l-value) out of them.
R-value references are a feature of C++11 and they were designed to both address move semantics and perfect forwarding issues
This is the plain-English explanation but if you want to thoroughly understand the problem, I'd suggest reading the following:
The problem:
We want some temporary values passed to a function F to be passed to another one E without any copy or assignment.
Attempts to solve this issue
If you try to pass it by reference like
template<typename T> void F(T& a) { E(a); }
you will not be able to use temporaries (they're not l-values)
F(1, 2, 3); // Won't work
Declaring a reference as const prolongs the lifetime of a temporary on the stack (this was historically done to avoid a common dangling reference error) so the following works
template<typename T> void E(const T& a) {}
template<typename T> void F(const T& a) {
E(a);
}
but the downside is that you'll have to modify the signature of the function(s) to conform to this solution
If we're interested in the signature of E (it should conform to something) but not in F's one, we might get away with
template<typename T> void E(T& a) {}
template<typename T> void F(const T& a) {
E(const_cast<T&>(a));
}
but in case this gets called with a real const and gets un-constant'ed, that would trigger undefined behavior
An unmaintainable solution could be to define all the variants you need
template<typename T> void E(T& a) {}
template<typename T> void F(T& a) { E(a); }
template<typename T> void F(const T& a) { E(const_cast<T&>(a)); }
but as the number of parameters grow, the number of combinations grows as well: this is likely to become unmaintainable
The solution in C++11
C++11 defines some rules that state
"[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."
in human-form (TR = a reference to type T, R = reference):
TR R
T& & -> T& // an lvalue reference to cv TR (becomes)-> lvalue reference to T
T& && -> T& // an rvalue reference to cv TR (becomes)-> TR (lvalue reference to T)
T&& & -> T& // an lvalue reference to cv TR (becomes)-> lvalue reference to T
T&& && -> T&& // an rvalue reference to cv TR (becomes)-> TR (rvalue reference to T)
The important takeaway here is that now you can keep track of the type the function received: you can receive an l-value and pass the same l-value to E or you can receive an r-value and pass the same r-value (after converting it since an l-value reference to whatever type reference becomes an l-value reference) to E:
template<typename T> void E(T&& a) {}
template<typename T> void F(T&& a) { E(static_cast<T&&>(a)); }
A synctactic sugar for
static_cast<T&&>(a)
is
std::forward<T>(a); // is the same as static_cast<T&&>(a);
so the final code that solves the problem and makes your life easier is
template<typename T> void E(T&& a) {}
template<typename T> void F(T&& a) { E(std::forward<T>(a)); }
Live example
References: Herb Sutter's blog and some other sources which unfortunately I can't find anymore. If anyone has a clue about those please write them in the comments below and I'll update the post. Thanks.
Dealing with r-value references and reference collapsing can be more complex than it initially appears.
Perfect forwarding
Perfect forwarding is there to ensure that the argument provided to a function is forwarded (passed) to another function with the same value category (basically r-value vs l-value) as originally provided.
It is typically used with template functions where reference collapsing may have taken place.
It can also be used within the same function.
Scott Meyers gives the following pseudo code in his Going Native 2013 presentation to explain the workings of std::forward (at approximately the 20 minute mark);
template <typename T>
T&& forward(T&& param) { // T&& here is formulated to disallow type deduction
if (is_lvalue_reference<T>::value) {
return param; // return type T&& collapses to T& in this case
}
else {
return move(param);
}
}
Example
An example from the site above, an archetypical example is that of make_unique
template<class T, class... U>
std::unique_ptr<T> make_unique(U&&... u)
{
return std::unique_ptr<T>(new T(std::forward<U>(u)...));
}
In the example, the arguments for the unique_ptr are provided to it through the make_unique as if they had been provided directly to unique_ptr, i.e. the reference, l-value and r-value nature of the arguments are maintained.
A more concrete example;
#include <iostream>
#include <utility>
#include <memory>
struct A {
// implementation excluded
};
struct B {
B(A &) // ctor 1
{
std::cout << "ctor 1" << std::endl;
}
B(A&&) // ctor 2
{
std::cout << "ctor 2" << std::endl;
}
};
int main()
{
A a;
auto b1 = std::make_unique<B>(a); // ctor 1 is used
auto b2 = std::make_unique<B>(A()); // ctor 2 is used
}
In Brief
Perfect forwarding depends on a handful of fundamental language constructs new to C++11 that form the bases for much of what we now see in generic programming:
Reference collapsing
Rvalue references
Move semantics
The use of std::forward is currently intended in the formulaic std::forward<T>, understanding how std::forward works helps understand why this is such, and also aids in identifying non-idiomatic or incorrect use of rvalues, reference collapsing and ilk.
Thomas Becker provides a nice, but dense write up on the perfect forwarding problem and solution.

Rvalue reference parameters and template functions

If I define a function which accepts an rvalue reference parameter:
template <typename T>
void fooT(T &&x) {}
I can call it, using GCC 4.5, with either a, ar, or arr:
int a, &ar = a, &&arr = 7;
fooT(a); fooT(ar); fooT(arr);
However, calling a similar, non-template function,
void fooInt(int &&x) {}
with any of those three arguments will fail. I was preparing to strengthen my knowledge of forward, but this has knocked me off course. Perhaps it's GCC 4.5; I was surprised to find that the first example from A Brief Introduction to Rvalue References also gives a compile error:
A a;
A&& a_ref2 = a; // an rvalue reference
The behavior of deduction in template parameters is unique, and is the reason your template version works. I've explained exactly how this deduction works here, in the context of another question.
Summarized: when the argument is an lvalue, T is deduced to T&, and T& && collapses to T&. And with the parameter at T&, it is perfectly valid to supply an lvalue T to it. Otherwise, T remains T, and the parameter is T&&, which accepts rvalues arguments.
Contrarily, int&& is always int&& (no template deduction rules to coerce it to something else), and can only bind to rvalues.
In addition to GMan's correct answer A Brief Introduction to Rvalue References has an incorrect example because it was written prior to a language change which outlawed:
A a;
A&& a_ref2 = a; // an rvalue reference (DISALLOWED in C++11)
Despite this change in the language, the main uses cases described in the article (move and forward) are still explained correctly in the article.
Update: Oh, and the same article was originally published here with (imho) slightly better formatting.