std::forward in non-template function [duplicate] - c++

This question already has answers here:
Usage of std::forward vs std::move
(3 answers)
Closed 5 years ago.
There is some function that has rvalue argument. There is an other function that also has rvalue argument. The first function calls the second function:
void inner(int&& a)
{/* ... */}
void outer(int&& a)
{
inner(std::move(a));
}
I used std::move because we use std::move to pass parameter as rvalue. But my colleague said that std::forward is more appropriate here because we forward (not move) rvalue reference here:
void outer(int&& a)
{
inner(std::forward(a));
}
Is it true? Which variant is more correct?

Forward is a conditional move. Using it here is like saying you should wrap statements in if (true).

The usage of forward is slightly different. It is used like this:
void outer(int&& a)
{
inner(std::forward<int>(a));
}
On non template code, forward is really confusing since there is no forwarding done.
Forwarding exist because of forwarding reference and reference collapsing. Take away those and no more forwarding magic.
In your case you're receiving an rvalue reference. Moving it is the right thing to do.
Forward is conditional move that depends on the declared type of the variable you might want to move. If that variable is an lvalue reference, std::forward<T>(t) won't move it.

Related

How does std::bind and std::thread copy the arguments that are perfectly forwarded? [duplicate]

This question already has answers here:
move semantics/behaviors in std::bind and std::thread
(1 answer)
Where in the source does gcc's std::bind copy arguments into a data structure?
(2 answers)
Closed 3 months ago.
There is something I miss about std::bind and std::thread. Internally, they both forward the rvalue reference template argument (perfect forwarding). But it is known that it copies the object passed to it, instead we wrapp the lvalue references in std::ref(). If you pass a lvalue like the following:
template <typename T>
void fct(T&& x)
{
f(std::forward(x));
}
...
int r{9};
fct(r);
In this case, r is perfectly forwarded as an lvalue.
So, where does the copy occurs, since in the source code, they always use std::forward?
I understand the reason behind the copying step, but how is it really implemented?
Also, this could help me to understand the use of std::ref in depth.
I thank you in advance for your help.

Why delete a non-member function? [duplicate]

This question already has an answer here:
What is the point of using delete on a non-member function?
(1 answer)
Closed 4 years ago.
This is about non-member functions. I do understand this as an implementation. But I have a bit of puzzlement with the logic behind?
// why this?
void do_not_use_this_ever ( void ) = delete ;
If I do not want a function to be used, why declare it and then delete it? Why not just:
// why not this?
// void do_not_use_this_ever ( void ) = delete ;
If = delete declares an intent, just a comment like above declares the same intent.
Can anyone think of a use-case where declaring a non-member function as deleted is better then not have it at all?
Update
Already answered here . Although. Both answers use std::cref as an example. As #geza said in the comment to his answer, it would be rather beneficial to discuss other use cases as well.
Deleting a non-member function can be useful to disable a function with certain parameters. For example, here is std::cref:
template< class T >
std::reference_wrapper<const T> cref( const T& t ) noexcept;
template <class T>
void cref(const T&&) = delete;
cref is used to convert an object reference to reference_wrapper. This can be used for example with std::bind: std::bind parameters are copied into the resulting object. But with cref, it becomes just a reference. So, cref must not be used with temporary parameters.
If the second overload wasn't deleted, then for example, cref(2) would be a valid expression (as a temporary can be bound to a const reference). This is a problem, as cref would return a reference to an object which will be destroyed. To disallow this, we need to delete functions where cref is passed a temporary, and this is what the second deleted overload does.

Usage of std::forward vs std::move

I always read that std::forward is only for use with template parameters. However, I was asking myself why. See the following example:
void ImageView::setImage(const Image& image){
_image = image;
}
void ImageView::setImage(Image&& image){
_image = std::move(image);
}
Those are two functions which basically do the same; one takes an l-value reference, the other an r-value reference. Now, I thought since std::forward is supposed to return an l-value reference if the argument is an l-value reference and an r-value reference if the argument is one, this code could be simplified to something like this:
void ImageView::setImage(Image&& image){
_image = std::forward(image);
}
Which is kind of similar to the example cplusplus.com mentions for std::forward (just without any template parameters). I'd just like to know, if this is correct or not, and if not why.
I was also asking myself what exactly would be the difference to
void ImageView::setImage(Image& image){
_image = std::forward(image);
}
You cannot use std::forward without explicitly specifying its template argument. It is intentionally used in a non-deduced context.
To understand this, you need to really understand how forwarding references (T&& for a deduced T) work internally, and not wave them away as "it's magic." So let's look at that.
template <class T>
void foo(T &&t)
{
bar(std::forward<T>(t));
}
Let's say we call foo like this:
foo(42);
42 is an rvalue of type int.
T is deduced to int.
The call to bar therefore uses int as the template argument for std::forward.
The return type of std::forward<U> is U && (in this case, that's int &&) so t is forwarded as an rvalue.
Now, let's call foo like this:
int i = 42;
foo(i);
i is an lvalue of type int.
Because of the special rule for perfect forwarding, when an lvalue of type V is used to deduce T in a parameter of type T &&, V & is used for deduction. Therefore, in our case, T is deduced to be int &.
Therefore, we specify int & as the template argument to std::forward. Its return type will therefore be "int & &&", which collapses to int &. That's an lvalue, so i is forwarded as an lvalue.
Summary
Why this works with templates is when you do std::forward<T>, T is sometimes a reference (when the original is an lvalue) and sometimes not (when the original is an rvalue). std::forward will therefore cast to an lvalue or rvalue reference as appropriate.
You cannot make this work in the non-template version precisely because you'll have only one type available. Not to mention the fact that setImage(Image&& image) would not accept lvalues at all—an lvalue cannot bind to rvalue references.
I recommend reading "Effective Modern C ++" by Scott Meyers, specifically:
Item 23: Understand std::move and std::forward.
Item 24: Distinguish universal references for rvalue references.
From a purely technical perspective, the answer is yes: std::forward
can do it all. std::move isn’t necessary. Of course, neither function
is really necessary, because we could write casts everywhere, but I
hope we agree that that would be, well, yucky. std::move’s attractions
are convenience, reduced likelihood of error, and greater clarity.
rvalue-reference
This function accepts rvalues and cannot accept lvalues.
void ImageView::setImage(Image&& image){
_image = std::forward(image); // error
_image = std::move(image); // conventional
_image = std::forward<Image>(image); // unconventional
}
Note first that std::move requires only a function argument, while std::forward requires both a function argument and a template type argument.
Universal references (forwarding references)
This function accepts all and does perfect forwarding.
template <typename T> void ImageView::setImage(T&& image){
_image = std::forward<T>(image);
}
You have to specify the template type in std::forward.
In this context Image&& image is always an r-value reference and std::forward<Image> will always move so you might as well use std::move.
Your function accepting an r-value reference cannot accept l-values so it is not equivalent to the first two functions.

C++ reference for both LValue and Rvalue without type deduction

I was reading a good tutorial on lvalue/rvalue references. If I've understood correctly when there is type deduction something like T&& can accept both an lvalue and an rvalue.
But is there a way to achieve that without a generic class? I'd like to avoid duplicating all my methods for accepting both lvalues and rvalues. And of course avoid passing big objects by value.
r-value references are mostly use in move-constructor and move assignment.
For regular method, you may stick with one reference type only:
For read only parameter (without copy), const reference is enough.
if you have to do a copy, you may take your argument by value and use std::move:
Example:
class Test
{
public:
void displayString(const std::string& s) const { std::cout << s << m_s; }
void setString(std::string s) { m_s = std::move(s); }
private:
std::string m_s;
};
If the function that you implement does not need rvalue semantic, then you can simply pass the argument by reference or by constant reference.
However, if you can take advantage of rvalues and do not want to duplicate your code, you can pass by value and move the result. That should be almost as efficient and can be more maintainable than code duplication or an implementation with universal references.
This answer shows the technique: Should all/most setter functions in C++11 be written as function templates accepting universal references?
// copy, then move
void set_a(A a_) { a = std::move(a_); }

Rvalue ref and perfect forwarding

I've read few papers about && and I'm just curious if having:
void fnc_1(int&& p)
{
//...
}
void fnc(int&& r)
{
fnc_1(r);//am I suppose to/should I? call it like so:fnc_1(std::forward(r))
}
or just passing 'r' is enough?
fnc_1(r) won't compile, because r is an lvalue, just like any other variable, regardless of type. Yes, that's right, named rvalue references are lvalues, not rvalues.
fnc_1(std::forward(r)) also won't compile, because std::forward is specifically designed not to infer its template argument.
To pass an rvalue, either of the following would work:
fnc_1(std::move(r))
fnc_1(std::forward<int&&>(r))
fnc_1(std::forward<int>(r))
Using std::move is the idiomatic way to cast an lvalue to an rvalue, so I would recommend using that.
The std::forward template is usually for dependent types. Please read this question carefully to see whether it applies here. This is a difficult subject to master, so feel free to update your question with relevant details about your exact problem (using rvalue references for integers isn't terribly exciting...).
I believe your question is about the understanding of the basic properties of rvalue references. The rule of thumb to remember is:
whatever has a name is a lvalue (const or not).
whatever has no name is a rvalue.
Types with && bind to rvalues.
If you have a function...
void foo(SomeClass&& x)
{
// ... then here x has type SomeClass& !
}
then inside the body, x is a name, and therefore a l value. It really has type SomeClass&. You must use std::move to turn a SomeClass& into SomeClass&&:
void bar(SomeClass&& x)
{
// Since `x` has a name here, it is a Lvalue.
// Therefore it has type SomeClass&, what the signature doesn't indicate.
// We thus have to explicitly turn it into a rvalue:
foo(std::move(x));
}