Pass by value or rvalue-ref - c++

For move enabled classes is there a difference between this two?
struct Foo {
typedef std::vector<std::string> Vectype;
Vectype m_vec;
//this or
void bar(Vectype&& vec)
{
m_vec = std::move(vec);
}
//that
void bar(Vectype vec)
{
m_vec = std::move(vec);
}
};
int main()
{
Vectype myvec{"alpha","beta","gamma"};
Foo fool;
fool.bar(std::move(myvec));
}
My understanding is that if you use a lvalue myvec you also required to introduce const
Vectype& version of Foo::bar() since Vectype&& won't bind. That's aside, in the rvalue case, Foo::bar(Vectype) will construct the vector using the move constructor or better yet elide the copy all together seeing vec is an rvalue (would it?). So is there a compelling reason to not to prefer by value declaration instead of lvalue and rvalue overloads?
(Consider I need to copy the vector to the member variable in any case.)

The pass-by-value version allows an lvalue argument and makes a copy of it. The rvalue-reference version can't be called with an lvalue argument.
Use const Type& when you don't need to change or copy the argument at all, use pass-by-value when you want a modifiable value but don't care how you get it, and use Type& and Type&& overloads when you want something slightly different to happen depending on the context.

The pass-by-value function is sufficient (and equivalent), as long as the argument type has an efficient move constructor, which is true in this case for std::vector.
Otherwise, using the pass-by-value function may introduce an extra copy-construction compared to using the pass-by-rvalue-ref function.
See the answer https://stackoverflow.com/a/7587151/1190077 to the related question Do I need to overload methods accepting const lvalue reference for rvalue references explicitly? .

Yes, the first one (Vectype&& vec) won't accept a const object or simply lvalue.
If you want to save the object inside like you do, it's best to copy(or move if you pass an rvalue) in the interface and then move, just like you did in your second example.

Related

Implementing rvalue references as parameters in function overloads

I've already asked on code review and software engineering but the topic didn't fit the site, so I'm asking here hoping this is not opinion-based. I am an "old school" C++ developer (I've stopped at C++ 2003) but now I've read a few books on modern C++ 11/17 and I'm rewriting some libraries of mine.
The first thing I've made is adding move constructor/assignment operator where needed ( = classes that already had destructor + copy constructor and copy assignment). Basically I'm using the rule of five.
Most of my functions are declared like
func(const std::string& s);
Which is the common way to pass a reference avoiding a copy. By the way there is also the new move semantic and there's somethig that I wasn't able to find in my books/online. This code:
void fun(std::string& x) {
x.append(" world");
std::cout << x;
}
int main()
{
std::string s{"Hello "};
fun(s);
}
Can also be written as:
void fun(std::string&& x) {
x.append(" world");
std::cout << x;
}
int main()
{
std::string s{"Hello "};
fun(std::move(s));
//or fun("Hello ");
// or fun(std::string {"Hello" });
}
My question is: when should I declare functions that accept a paramenter that is a rvalue reference?
I understand the usage of && semantic on constructors and assignment operators but not really on functions. In the example above (first function) I have a std::string& x which cannot be called as fun("Hello "); of course because I should delcare the type as const std::string& x. But now the const doesnt allow me to change the string!
Yes, I could use a const cast but I rarely do casts (and if it's the case, they're dynamic casts). The power of the && is that I avoid copies, I don't have to do something like
std::string x = "...";
fun(x); //void fun(std::string& x) {}
and I can assing temporary values that will be moved. Should I declare functions with rvalue references when possible?
I have a library that I'm rewriting with modern C++ 17 and I have functions like:
//only const-ref
Type1 func(const type2& x);
Type3 function(const type4& x);
I am asking if it's worth rewriting all of them as
//const-ref AND rvalue reference
Type1 func(const type2& x);
Type3 function(const type4& x);
Type1 func(type2&& x);
Type3 function(type4&& x);
I don't want to create too many overloads that may be useless but if an user of my library wanted to use the move operation I should create the && param types. Of course I am not doing this for primitive types (int, double, char...) but for containers or classes. What do you suggest?
I am not sure if the latter scenario (with both versions) would be useful or not.
Let me comment on four scenarios in your question and examples.
std::string_view with pass-by-value is supposed to replace const std::string& parameters and whenever you can guarantee the necessary preconditions for a safe usage of std::string_view (lifetime, pointee doesn't change), it's a good candidate to start modernizing your function signatures.
const T& vs. T&& (where T is not subject to template type deduction) with known usage scenarios. The void fun function that appends to a given, modifiable string, will only makes sense as void fun(std::string&&) if calling code doesn't need the result after the call. In this case, the rvalue-reference signature documents this expectation nicely and is the way to go. But these cases are rather rare in my experience.
const T& vs. T&& (again, no type deduction) with unknown usage scenarios. A good reference here is std::vector::push_back, which is overloaded for both rvalue and lvalue references. The push_back operation is assumed to be cheap compared to move-construction a T, that's why the overload makes sense. When a function is assumed to be more expensive than such a move-construction, passing the argument by value is a simplification that can make sense (see also Item 41 in EMC++).
const T& vs. T&& when type deduction takes place. Here, use universal references together with std::forward whenever possible and the parameters can't be const qualified. If they aren't modified in the function body, go with const T&.
You want to use rvalue references only if:
You might retain a copy and you need the extra performance (measure!)
Example for this would be writing a library type (e.g. std::vector) where performance matters to its users.
You want only temporaries to be passed to your function
Example for this is the move assignment operator: After the assignment, the original objects state will not exist anymore.
Forwarding references (T&& with T deduced) fall under the first option.
Rvalue reference (not to be confused with a forwarding reference!) in function arguments is used when there is a need to move ownership from one object to another.
It is true that it is often done in context of move constructors/assignment operators, but this is not the only case. For example, a function accepting an ownership of std::unique_prt could accept it's argument by an rvalue reference.

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_); }

C++11 move(x) actually means static_cast<X&&>(x)? [duplicate]

This question already has answers here:
When is the move constructor called in the `std::move()` function?
(2 answers)
Closed 9 years ago.
Just reading Stroustrup's C++ Programming Language 4th Ed and in chapter 7 he says:
move(x) means static_cast<X&&>(x) where X is the type of x
and
Since move(x) does not move x (it simply produces an rvalue reference
to x) it would have been better if move() had been called rval()
My question is, if move() just turns the variable in to an rval, what is the actual mechanism which achieves the "moving" of the reference to the variable (by updating the pointer)??
I thought move() is just like a move constructor except the client can use move() to force the compiler??
what is the actual mechanism which achieves the "moving" of the reference to the variable (by updating the pointer)??
Passing it to a function (or constructor) that takes an rvalue reference, and moves the value from that reference. Without the cast, variables cannot bind to rvalue references, and so can't be passed to such a function - this prevents variables from being accidentally moved from.
I thought move() is just like a move constructor except the client can use move() to force the compiler??
No; it's used to convert an lvalue into an rvalue in order to pass it to a move constructor (or other moving function) which requires an rvalue reference.
typedef std::unique_ptr<int> noncopyable; // Example of a noncopyable type
noncopyable x;
noncopyable y(x); // Error: no copy constructor, and can't implicitly move from x
noncopyable z(std::move(x)); // OK: convert to rvalue, then use move constructor
When you are calling move, you are just telling "Hey, I want to move this object". And when constructor accepts rvalue-reference, it understands it as "Hmm, someone want I move data from this object into myself. So, OK, I'll do it".
std::move does not moves or changes object, it just "marks" it as "ready-for-moving". And only function, that accepts rvalue reference should implement moving actual object.
This is an example, that describes the text above:
#include <iostream>
#include <utility>
class Foo
{
public:
Foo(std::size_t n): _array(new int[n])
{
}
Foo(Foo&& foo): _array(foo._array)
{
// Hmm, someone tells, that this object is no longer needed
// I will move it into myself
foo._array = nullptr;
}
~Foo()
{
delete[] _array;
}
private:
int* _array;
};
int main()
{
Foo f1(5);
// Hey, constructor, I want you move this object, please
Foo f2(std::move(f1));
return 0;
}
As in Going Native 2013, Scott Meyers gave the talk about C++ 11 features, including move.
What std::move essentially do is "unconditionally casts to a rvalue".
My question is, if move() just turns the variable in to an rval, what is the actual mechanism which achieves the "moving" of the reference to the variable (by updating the pointer)??
move does the type casting, thus the compiler will know which ctor to use. The actual move operation is done by the move ctor. You can take it as a function overloading. (ctor overloads with the rvalue parameter type.)
rvalues are generally temporary values which are discarded and destroyed immediately after creation (with a few exceptions). std::string&& is a reference to a std::string that will only bind to an rvalue. Prior to C++11, temporaries would only bind to std::string const& -- after C++11, they also bind to std::string&&.
A variable of type std::string&& behaves much like a bog-standard reference. It is pretty much only in the binding of function signatures and initialization that std::string&& differs from std::string& variables. The other way it differs is when you decltype the reference. All other uses are unchanged.
On the other hand, if a function returns a std::string&&, it is very different than returning a std::string&, because the second kind of thing that can be bound to a std::string&& is the return value of a function returning std::string&&.
std::move is the most common way to generate such a function. In a sense, it lies to the context it is in and tells it "I am a temporary, do with me what you will". So std::move takes a reference to something, and does a cast that makes it pretend to be a temporary -- aka, rvalue.
Move constructors and move assignment and other move-aware functions take an rvalue reference to know when the data they are passed is "scratch" data that they can "damage" to some extent when using it. This is very useful because many types (from containers, to std::function, to anything that uses the pImpl pattern, to non-copyable resources) can have their internal state moved much easier than it can be copied. Such a move changes the state of the source object: but because the function is told it is scratch data, that isn't impolite.
So the move happens not in std::move, but in the function that understands that the return value of std::move implies that it is permitted to modify the data in a somewhat destructive manner if that would help it.
The other ways you can get an rvalue, or an indication that the source object is "scratch data", is when you have a true temporary (an anonymous object created as the return of some other function, or one created using function-style constructor syntax), or when you return from a function with a statement of the form return local_variable;. In both cases, the data binds to rvalue references.
The short version is that std::move does not move, and std::forward does not forward, it just indicates that such an action would be allowed at this point, and lets the function/constructor being called decide what to do with that information.
from http://en.cppreference.com/w/cpp/utility/move
std::move obtains an rvalue reference to its argument and converts it
to an xvalue.
Code that receives such an xvalue has the opportunity to
optimize away unnecessary overhead by moving data out of the argument,
leaving it in a valid but unspecified state.
Return value
static_cast<typename std::remove_reference<T>::type&&>(t)
you can see move is just static_cast
by calling std::move on an object doesn't really doing anything useful, however it tells that the return value can be modified to "a valid but unspecified state"
I thought move() is just like a move constructor except the client can
use move() to force the compiler??
By essentially casting the type to an r-value type, this allows the compiler to invoke the move constructor over the copy constructor.
std::move is equivalent to static_cast<std::string&&>(x).
In the standard, it is defined like this:
template <class T>
constexpr remove_reference_t<T>&& move(T&&) noexcept;
Complementing other answers, an example could help you to better understand how rvalue references work. Take a look to the following code that emulates rvalue references:
#include <iostream>
#include <memory>
template <class T>
struct rvalue_ref
{
rvalue_ref(T& obj) : obj_ptr{std::addressof(obj)} {}
T* operator->() //For simplicity, we'll use the reference as a pointer.
{ return obj_ptr; }
T* obj_ptr;
};
template <class T>
rvalue_ref<T> move(T& obj)
{
return rvalue_ref<T>(obj);
}
template <class T>
struct myvector
{
myvector(unsigned sz) : data{new T[sz]} {}
myvector(rvalue_ref<myvector> other) //Move constructor
{
this->data = other->data;
other->data = nullptr;
}
~myvector()
{
delete[] data;
}
T* data;
};
int main()
{
myvector<int> vec(5); //vector of five integers
std::cout << vec.data << '\n'; //Print address of data
myvector<int> vec2 = move(vec); //Move data from vec to vec2
std::cout << vec.data << '\n'; //Prints zero
//Prints address of moved data (same as first output line)
std::cout << vec2.data << '\n';
}
As we can see, "move" only generates the correct alias, to indicate to the compiler which constructor overload want to use. The difference between this implementation and real rvalue references is of course that casting to rvalue reference has zero overhead, since it's only a compiler directive.

What does auto&& tell us?

If you read code like
auto&& var = foo();
where foo is any function returning by value of type T. Then var is an lvalue of type rvalue reference to T. But what does this imply for var? Does it mean, we are allowed to steal the resources of var? Are there any reasonable situations when you should use auto&& to tell the reader of your code something like you do when you return a unique_ptr<> to tell that you have exclusive ownership? And what about for example T&& when T is of class type?
I just want to understand, if there are any other use cases of auto&& than those in template programming; like the ones discussed in the examples in this article Universal References by Scott Meyers.
By using auto&& var = <initializer> you are saying: I will accept any initializer regardless of whether it is an lvalue or rvalue expression and I will preserve its constness. This is typically used for forwarding (usually with T&&). The reason this works is because a "universal reference", auto&& or T&&, will bind to anything.
You might say, well why not just use a const auto& because that will also bind to anything? The problem with using a const reference is that it's const! You won't be able to later bind it to any non-const references or invoke any member functions that are not marked const.
As an example, imagine that you want to get a std::vector, take an iterator to its first element and modify the value pointed to by that iterator in some way:
auto&& vec = some_expression_that_may_be_rvalue_or_lvalue;
auto i = std::begin(vec);
(*i)++;
This code will compile just fine regardless of the initializer expression. The alternatives to auto&& fail in the following ways:
auto => will copy the vector, but we wanted a reference
auto& => will only bind to modifiable lvalues
const auto& => will bind to anything but make it const, giving us const_iterator
const auto&& => will bind only to rvalues
So for this, auto&& works perfectly! An example of using auto&& like this is in a range-based for loop. See my other question for more details.
If you then use std::forward on your auto&& reference to preserve the fact that it was originally either an lvalue or an rvalue, your code says: Now that I've got your object from either an lvalue or rvalue expression, I want to preserve whichever valueness it originally had so I can use it most efficiently - this might invalidate it. As in:
auto&& var = some_expression_that_may_be_rvalue_or_lvalue;
// var was initialized with either an lvalue or rvalue, but var itself
// is an lvalue because named rvalues are lvalues
use_it_elsewhere(std::forward<decltype(var)>(var));
This allows use_it_elsewhere to rip its guts out for the sake of performance (avoiding copies) when the original initializer was a modifiable rvalue.
What does this mean as to whether we can or when we can steal resources from var? Well since the auto&& will bind to anything, we cannot possibly try to rip out vars guts ourselves - it may very well be an lvalue or even const. We can however std::forward it to other functions that may totally ravage its insides. As soon as we do this, we should consider var to be in an invalid state.
Now let's apply this to the case of auto&& var = foo();, as given in your question, where foo returns a T by value. In this case we know for sure that the type of var will be deduced as T&&. Since we know for certain that it's an rvalue, we don't need std::forward's permission to steal its resources. In this specific case, knowing that foo returns by value, the reader should just read it as: I'm taking an rvalue reference to the temporary returned from foo, so I can happily move from it.
As an addendum, I think it's worth mentioning when an expression like some_expression_that_may_be_rvalue_or_lvalue might turn up, other than a "well your code might change" situation. So here's a contrived example:
std::vector<int> global_vec{1, 2, 3, 4};
template <typename T>
T get_vector()
{
return global_vec;
}
template <typename T>
void foo()
{
auto&& vec = get_vector<T>();
auto i = std::begin(vec);
(*i)++;
std::cout << vec[0] << std::endl;
}
Here, get_vector<T>() is that lovely expression that could be either an lvalue or rvalue depending on the generic type T. We essentially change the return type of get_vector through the template parameter of foo.
When we call foo<std::vector<int>>, get_vector will return global_vec by value, which gives an rvalue expression. Alternatively, when we call foo<std::vector<int>&>, get_vector will return global_vec by reference, resulting in an lvalue expression.
If we do:
foo<std::vector<int>>();
std::cout << global_vec[0] << std::endl;
foo<std::vector<int>&>();
std::cout << global_vec[0] << std::endl;
We get the following output, as expected:
2
1
2
2
If you were to change the auto&& in the code to any of auto, auto&, const auto&, or const auto&& then we won't get the result we want.
An alternative way to change program logic based on whether your auto&& reference is initialised with an lvalue or rvalue expression is to use type traits:
if (std::is_lvalue_reference<decltype(var)>::value) {
// var was initialised with an lvalue expression
} else if (std::is_rvalue_reference<decltype(var)>::value) {
// var was initialised with an rvalue expression
}
First, I recommend reading this answer of mine as a side-read for a step-by-step explanation on how template argument deduction for universal references works.
Does it mean, we are allowed to steal the resources of var?
Not necessarily. What if foo() all of a sudden returned a reference, or you changed the call but forgot to update the use of var? Or if you're in generic code and the return type of foo() might change depending on your parameters?
Think of auto&& to be exactly the same as the T&& in template<class T> void f(T&& v);, because it's (nearly†) exactly that. What do you do with universal references in functions, when you need to pass them along or use them in any way? You use std::forward<T>(v) to get the original value category back. If it was an lvalue before being passed to your function, it stays an lvalue after being passed through std::forward. If it was an rvalue, it will become an rvalue again (remember, a named rvalue reference is an lvalue).
So, how do you use var correctly in a generic fashion? Use std::forward<decltype(var)>(var). This will work exactly the same as the std::forward<T>(v) in the function template above. If var is a T&&, you'll get an rvalue back, and if it is T&, you'll get an lvalue back.
So, back on topic: What do auto&& v = f(); and std::forward<decltype(v)>(v) in a codebase tell us? They tell us that v will be acquired and passed on in the most efficient way. Remember, though, that after having forwarded such a variable, it's possible that it's moved-from, so it'd be incorrect use it further without resetting it.
Personally, I use auto&& in generic code when I need a modifyable variable. Perfect-forwarding an rvalue is modifying, since the move operation potentially steals its guts. If I just want to be lazy (i.e., not spell the type name even if I know it) and don't need to modify (e.g., when just printing elements of a range), I'll stick to auto const&.
† auto is in so far different that auto v = {1,2,3}; will make v an std::initializer_list, whilst f({1,2,3}) will be a deduction failure.
Consider some type T which has a move constructor, and assume
T t( foo() );
uses that move constructor.
Now, let's use an intermediate reference to capture the return from foo:
auto const &ref = foo();
this rules out use of the move constructor, so the return value will have to be copied instead of moved (even if we use std::move here, we can't actually move through a const ref)
T t(std::move(ref)); // invokes T::T(T const&)
However, if we use
auto &&rvref = foo();
// ...
T t(std::move(rvref)); // invokes T::T(T &&)
the move constructor is still available.
And to address your other questions:
... Are there any reasonable situations when you should use auto&& to tell the reader of your code something ...
The first thing, as Xeo says, is essentially I'm passing X as efficiently as possible, whatever type X is. So, seeing code which uses auto&& internally should communicate that it will use move semantics internally where appropriate.
... like you do when you return a unique_ptr<> to tell that you have exclusive ownership ...
When a function template takes an argument of type T&&, it's saying it may move the object you pass in. Returning unique_ptr explicitly gives ownership to the caller; accepting T&& may remove ownership from the caller (if a move ctor exists, etc.).
The auto && syntax uses two new features of C++11:
The auto part lets the compiler deduce the type based on the context (the return value in this case). This is without any reference qualifications (allowing you to specify whether you want T, T & or T && for a deduced type T).
The && is the new move semantics. A type supporting move semantics implements a constructor T(T && other) that optimally moves the content in the new type. This allows an object to swap the internal representation instead of performing a deep copy.
This allows you to have something like:
std::vector<std::string> foo();
So:
auto var = foo();
will perform a copy of the returned vector (expensive), but:
auto &&var = foo();
will swap the internal representation of the vector (the vector from foo and the empty vector from var), so will be faster.
This is used in the new for-loop syntax:
for (auto &item : foo())
std::cout << item << std::endl;
Where the for-loop is holding an auto && to the return value from foo and item is a reference to each value in foo.

C++0x rvalue references - lvalues-rvalue binding

This is a follow-on question to
C++0x rvalue references and temporaries
In the previous question, I asked how this code should work:
void f(const std::string &); //less efficient
void f(std::string &&); //more efficient
void g(const char * arg)
{
f(arg);
}
It seems that the move overload should probably be called because of the implicit temporary, and this happens in GCC but not MSVC (or the EDG front-end used in MSVC's Intellisense).
What about this code?
void f(std::string &&); //NB: No const string & overload supplied
void g1(const char * arg)
{
f(arg);
}
void g2(const std::string & arg)
{
f(arg);
}
It seems that, based on the answers to my previous question that function g1 is legal (and is accepted by GCC 4.3-4.5, but not by MSVC). However, GCC and MSVC both reject g2 because of clause 13.3.3.1.4/3, which prohibits lvalues from binding to rvalue ref arguments. I understand the rationale behind this - it is explained in N2831 "Fixing a safety problem with rvalue references". I also think that GCC is probably implementing this clause as intended by the authors of that paper, because the original patch to GCC was written by one of the authors (Doug Gregor).
However, I don't this is quite intuitive. To me, (a) a const string & is conceptually closer to a string && than a const char *, and (b) the compiler could create a temporary string in g2, as if it were written like this:
void g2(const std::string & arg)
{
f(std::string(arg));
}
Indeed, sometimes the copy constructor is considered to be an implicit conversion operator. Syntactically, this is suggested by the form of a copy constructor, and the standard even mentions this specifically in clause 13.3.3.1.2/4, where the copy constructor for derived-base conversions is given a higher conversion rank than other user-defined conversions:
A conversion of an expression of class type to the same class type is given Exact Match rank, and a conversion
of an expression of class type to a base class of that type is given Conversion rank, in spite of the fact that
a copy/move constructor (i.e., a user-defined conversion function) is called for those cases.
(I assume this is used when passing a derived class to a function like void h(Base), which takes a base class by value.)
Motivation
My motivation for asking this is something like the question asked in How to reduce redundant code when adding new c++0x rvalue reference operator overloads ("How to reduce redundant code when adding new c++0x rvalue reference operator overloads").
If you have a function that accepts a number of potentially-moveable arguments, and would move them if it can (e.g. a factory function/constructor: Object create_object(string, vector<string>, string) or the like), and want to move or copy each argument as appropriate, you quickly start writing a lot of code.
If the argument types are movable, then one could just write one version that accepts the arguments by value, as above. But if the arguments are (legacy) non-movable-but-swappable classes a la C++03, and you can't change them, then writing rvalue reference overloads is more efficient.
So if lvalues did bind to rvalues via an implicit copy, then you could write just one overload like create_object(legacy_string &&, legacy_vector<legacy_string> &&, legacy_string &&) and it would more or less work like providing all the combinations of rvalue/lvalue reference overloads - actual arguments that were lvalues would get copied and then bound to the arguments, actual arguments that were rvalues would get directly bound.
Clarification/edit: I realize this is virtually identical to accepting arguments by value for movable types, like C++0x std::string and std::vector (save for the number of times the move constructor is conceptually invoked). However, it is not identical for copyable, but non-movable types, which includes all C++03 classes with explicitly-defined copy constructors. Consider this example:
class legacy_string { legacy_string(const legacy_string &); }; //defined in a header somewhere; not modifiable.
void f(legacy_string s1, legacy_string s2); //A *new* (C++0x) function that wants to move from its arguments where possible, and avoid copying
void g() //A C++0x function as well
{
legacy_string x(/*initialization*/);
legacy_string y(/*initialization*/);
f(std::move(x), std::move(y));
}
If g calls f, then x and y would be copied - I don't see how the compiler can move them. If f were instead declared as taking legacy_string && arguments, it could avoid those copies where the caller explicitly invoked std::move on the arguments. I don't see how these are equivalent.
Questions
My questions are then:
Is this a valid interpretation of the standard? It seems that it's not the conventional or intended one, at any rate.
Does it make intuitive sense?
Is there a problem with this idea that I"m not seeing? It seems like you could get copies being quietly created when that's not exactly expected, but that's the status quo in places in C++03 anyway. Also, it would make some overloads viable when they're currently not, but I don't see it being a problem in practice.
Is this a significant enough improvement that it would be worth making e.g. an experimental patch for GCC?
What about this code?
void f(std::string &&); //NB: No const string & overload supplied
void g2(const std::string & arg)
{
f(arg);
}
...However, GCC and MSVC both reject g2 because of clause 13.3.3.1.4/3, which prohibits lvalues from binding to rvalue ref arguments. I understand the rationale behind this - it is explained in N2831 "Fixing a safety problem with rvalue references". I also think that GCC is probably implementing this clause as intended by the authors of that paper, because the original patch to GCC was written by one of the authors (Doug Gregor)....
No, that's only half of the reason why both compilers reject your code. The other reason is that you can't initialize a reference to non-const with an expression referring to a const object. So, even before N2831 this didn't work. There is simply no need for a conversion because a string is a already a string. It seems you want to use string&& like string. Then, simply write your function f so that it takes a string by value. If you want the compiler to create a temporary copy of a const string lvalue just so you can invoke a function taking a string&&, there wouldn't be a difference between taking the string by value or by rref, would it?
N2831 has little to do with this scenario.
If you have a function that accepts a number of potentially-moveable arguments, and would move them if it can (e.g. a factory function/constructor: Object create_object(string, vector, string) or the like), and want to move or copy each argument as appropriate, you quickly start writing a lot of code.
Not really. Why would you want to write a lot of code? There is little reason to clutter all your code with const&/&& overloads. You can still use a single function with a mix of pass-by-value and pass-by-ref-to-const -- depending on what you want to do with the parameters. As for factories, the idea is to use perfect forwarding:
template<class T, class... Args>
unique_ptr<T> make_unique(Args&&... args)
{
T* ptr = new T(std::forward<Args>(args)...);
return unique_ptr<T>(ptr);
}
...and all is well. A special template argument deduction rule helps differentiating between lvalue and rvalue arguments and std::forward allows you to create expressions with the same "value-ness" as the actual arguments had. So, if you write something like this:
string foo();
int main() {
auto ups = make_unique<string>(foo());
}
the string that foo returned is automatically moved to the heap.
So if lvalues did bind to rvalues via an implicit copy, then you could write just one overload like create_object(legacy_string &&, legacy_vector &&, legacy_string &&) and it would more or less work like providing all the combinations of rvalue/lvalue reference overloads...
Well, and it would be pretty much equivalent to a function taking the parameters by value. No kidding.
Is this a significant enough improvement that it would be worth making e.g. an experimental patch for GCC?
There's no improvement.
I don't quite see your point in this question. If you have a class that is movable, then you just need a T version:
struct A {
T t;
A(T t):t(move(t)) { }
};
And if the class is traditional but has an efficient swap you can write the swap version or you can fallback to the const T& way
struct A {
T t;
A(T t) { swap(this->t, t); }
};
Regarding the swap version, I would rather go with the const T& way instead of that swap. The main advantage of the swap technique is exception safety and is to move the copy closer to the caller so that it can optimize away copies of temporaries. But what do you have to save if you are just constructing the object anyway? And if the constructor is small, the compiler can look into it and can optimize away copies too.
struct A {
T t;
A(T const& t):t(t) { }
};
To me, it doesn't seem right to automatically convert a string lvalue to a rvalue copy of itself just to bind to a rvalue reference. An rvalue reference says it binds to rvalue. But if you try binding to an lvalue of the same type it better fails. Introducing hidden copies to allow that doesn't sound right to me, because when people see a X&& and you pass a X lvalue, I bet most will expect that there is no copy, and that binding is directly, if it works at all. Better fail out straight away so the user can fix his/her code.