In Item 41, Scott Meyers writes the following two classes:
class Widget {
public:
void addName(const std::string& newName) // take lvalue;
{ names.push_back(newName); } // copy it
void addName(std::string&& newName) // take rvalue;
{ names.push_back(std::move(newName)); } // move it; ...
private:
std::vector<std::string> names;
};
class Widget {
public:
template<typename T> // take lvalues
void addName(T&& newName) // and rvalues;
{ // copy lvalues,
names.push_back(std::forward<T>(newName)); } // move rvalues;
} // ...
private:
std::vector<std::string> names;
};
What's written in the comments is correct, even if it doesn't mean at all that the two solutions are equivalent, and some of the differences are indeed discussed in the book.
In the errata, however, the author comments another difference not discussed in the book:
Another behavioral difference between (1) overloading for lvalues and rvalues and (2) a template taking a universal reference (uref) is that the lvalue overload declares its parameter const, while the uref approach doesn't. This means that functions invoked on the lvalue overload's parameter will always be the const versions, while functions invoked on the uref version's parameter will be the const versions only if the argument passed in is const. In other words, non-const lvalue arguments may yield different behavior in the overloading design vis-a-vis the uref design.
But I'm not sure I understand it.
Actually, writing this question I've probably understood, but I'm not writing an answer as I'm still not sure.
Probably the author is saying that when a non-const lvalue is passed to addName, newName is const in the first code, and non-const in the second code, which means that if newName was passed to another function (or a member function was called on it), than that function would be required to take a const parameter (or be a const member function).
Have I interpreted correctly?
However, I don't see how this makes a difference in the specific example, since no member function is called on newName, nor it is passed to a function which has different overloads for const and non-const parameters (not exactly: std::vector<T>::push_back has two overloads for const T& arguments and T&& arguments`, but an lvalue would still bind only to the former overload...).
In the second scenario, when a const std::string lvalue is passed to the template
template<typename T>
void addName(T&& newName)
{ names.push_back(std::forward<T>(newName)); }
the instantiation results in the following (where I've removed the std::forward call as it is, in practice, a no-op)
void addName(const std::string& newName)
{ names.push_back(newName); }
whereas if a std::string lvalue is passed, then the resulting instance of addName is
void addName(std::string& newName)
{ names.push_back(newName); }
which means that the non-const version of std::vector<>::push_back is called.
In the first scenario, when a std::string lvalue is passed to addName, the first overload is picked regardless of the const-ness
void addName(const std::string& newName)
{ names.push_back(newName); }
which means that the const overload of std::vector<>::push_back is selected in both cases.
Related
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.
I'm trying to understand the performance implications of using WidgetURef::setName (URef being a Universal Reference, the term coined by Scott Meyers) vs WidgedRRef::setName (RRef being an R-value Reference):
#include <string>
class WidgetURef {
public:
template<typename T>
void setName(T&& newName)
{
name = std::move(newName);
}
private:
std::string name;
};
class WidgetRRef {
public:
void setName(std::string&& newName)
{
name = std::move(newName);
}
private:
std::string name;
};
int main() {
WidgetURef w_uref;
w_uref.setName("Adela Novak");
WidgetRRef w_rref;
w_rref.setName("Adela Novak");
}
I do appreciate that with universal references one should be using std::forward instead, but this is just an (imperfect) example to highlight the interesting bit.
Question
In this particular example, what is the performance implications of using one implementation vs the other? Although WidgetURef requires type deduction, it's otherwise identical to WidgetRRef, isn't it? At least in this particular scenario, in both cases the argument is an r-value reference, so no temporaries are created. Is this reasoning correct?
Context
The example was taken from Item25 of Scott Meyers' "Effective Modern C++" (p. 170). According to the book (provided that my understanding is correct!), the version taking a universal reference T&& doesn't require temporary objects and the other one, taking std::string&&, does. I don't really see why.
setName(T&& newName) with argument "Adela Novak" gets T duduced as const char (&)[12] which is then assigned to std::string.
setName(std::string&& newName) with argument "Adela Novak" creates a temporary std::string object which is then move assigned to std::string.
The first one is more efficient here because there is no moving involved.
In this particular example, what is the performance implications of using one implementation vs the other?
Universal references, as Scott Meyers calls them, are not primarily there for performance reasons, but, loosely speaking, to treat both L- and Rvalue references in the same manner to avoid countless overloads (and for being able to propagate all type information during forwarding).
[...] so no temporaries are created. Is this reasoning correct?
Rvalue references do not prevent temporaries from being created. Rvalue references are the kind of references that are able to be bound to temporaries (apart from const lvalue references)! Of course, in your example, there will be temporaries, but the rvalue reference can bind to it. The universal reference first has to undergo the reference collapsing but in the end, the behaviour will be identical in your case:
// explicitly created temporary
w_uref.setName(std::string("Adela Novak"));
// will create temporary of std::string --> uref collapses to rvalue ref
// so is effectively the same as
w_rref.setName("Adela Novak");
By using the rvalue reference on the other hand, you force a temporary implicitly as std::string&& cannot bind to that literal.
w_rref.setName("Adela Novak"); // need conversion
So the compiler will create a temporary std::string from the literal the rvalue reference then can bind to.
I don't really see why.
In this case, the template will be resolved to const char(&)[12] and thus, no std::string temporary will be created in contrast to the case above. This therefore is more efficient.
Scott himself says that WidgetURef "compiles, but is bad, bad, bad!" (verbatim). These two classes behave differently as you use std::move instead of std::forward: setName therefore can modify its argument:
#include <string>
#include <iostream>
class WidgetURef {
public:
template<typename T>
void setName(T&& newName)
{
name = std::move(newName);
}
private:
std::string name;
};
int main() {
WidgetURef w_uref;
std::string name = "Hello";
w_uref.setName(name);
std::cout << "name=" << name << "\n";
}
can easily print name=, meaning that the value of name was changed. And indeed it does on ideone at the very least.
On the other hand, WidgetRRef requires that the passed argument is a rvalue-reference, so the example above wouldn't compile without explicit setName(std::move(name)).
Neither WidgetURef, nor WidgetRRef require creating extra copies if you pass std::string as an argument. However, if you pass something which std::string can be assigned from (such as const char*), then the first example will pass that by reference and assign it to string (without any copies except for copying data from C-style string into std::string), and the second example will first create a temporary string, and then pass it as an rvalue reference to the method. These properties preserve if you replace std::move(newName) with a correct std::forward<T>(newName).
Assuming the arguments as stated in the question
template<typename T>
void setName(T&& newName)
{
name = std::forward<T>(newName);
}
Will invoke the std::string assignment operator for the data member name with a const char * argument
void setName(std::string&& newName)
{
name = std::move(newName);
}
Invokes std::string constructor to create a temporary, to which the Rvalue Ref can bind to.
Invokes std::string move assignment / constructor for the data member name with a std::string&& argument
Invokes std::string destructor to destroy the temporary, from which we moved the data.
Working from the Efficient Modern C++, Item 25. we have an example
Case 1
class Widget {
public:
template<typename T>
void setName(T&& newName)
{ name = std::forward<T>(newName); }
...
};
Case 2
class Widget {
public:
void setName(const std::string& newName)
{ name = newName; }
void setName(std::string&& newName)
{ name = std::move(newName); }
...
};
The call
Widget w;
w.setName("Adela Novak");
Now assuming case 1, the book states that the literal is conveyed to the assignment operator for t std::string inside w's name data member.
Assuming case 2, the book states that -> first a temporary is created from the literal, calling the string constructor, so the setName parameter can bind to it, and than this temporary is moved into w's name data member.
Question
Why does this difference in behavior come about and how am I to think about it?
Namely, why is there no need for a temporary in case 1? Why is there difference? Is T&& not deduced to be an rvalue reference to a string, thus arriving at the same behavior as case 2 (obviously not, as per the book, but why)?
In case 1, T is deduced to be const char (&)[12], not std::string. There is no reason for the compiler to promote the string literal to std::string yet. In case 2, every overload requires takes a reference to an std::string, which forces the creation of a temporary std::string to which a reference can be bound using the implicit const char* constructor.
Note that while an rvalue reference such as std::string && may only bind to an rvalue, the templated equivalent T && may bind to both rvalues and lvalues.
I'm working on g++ and here I have tried to overload a function by just adding const to parameter. It works fine and when it runs, it calls the function without const
Is this behavior specified in the C++ standard?
What the reason it calls the function without const
void print(const std::string& str){std::cout << "const" << str << std::endl;}
void print(std::string& str){std::cout << str << std::endl;}
int main()
{
std::string temp = "hello";
print(temp);
return 0;
}
Reference bindings are an identity category §13.3.3.1.4) but since the latter is more cv-qualified, for §13.3.3.2, the non-const is preferred (sample code from the standard):
int f(const int &);
int f(int &);
int i;
int j = f(i); // calls f(int &)
That is standard behavior. Any other behavior would lead to crazy behavior. In particular, the non-const function would not be callable at all.
const is part of method signature. Overriding works only for methods with the same signature.
This behavior was made to avoid reverse situation when you use const method of base class to call not const method of child class.
The reason is this section in [over.ics.rank]/3 where this case is explicitly covered:
Standard conversion sequence S1 is a better conversion sequence than
standard conversion sequence S2 if […] — S1 and S2 are reference
bindings (8.5.3), and the types to which the references refer are the
same type except for top-level cv-qualifiers, and the type to which
the reference initialized by S2 refers is more cv-qualified than the
type to which the reference initialized by S1 refers.
S1 corresponds to the second overload and S2 to the first.
What the reason it calls the function without const
You always try to select the most specialized thing. That is the case in overload resolution just as it is in partial ordering of function templates. The second overload is more specialized than the first because the first can be called with arguments which the second cannot be called with - that is the basic reasoning behind this rule.
Overloading works by matching the types of the arguments, including the qualifiers. In your case temp has type std::string not const std::string. You have only initialised it with a literal constant, it is not itself constant.
Consider the following:
std::string temp( "hello" ) ;
print(temp); // hello
print( std::string("hello") ) ; // consthello
print( "hello" ) ; // consthello
print( static_cast<const std::string>(temp) ) ; // consthello
const std::string temp2( "hello" ) ;
print(temp2); // consthello
If you were to remove the non-const version, all three will call the remaining const overload. In this example, only the const version is in fact necessary (and preferred) since neither version modify the string object.
If on the other hand you removed the non-const version, there would be no function matching any but the first example above, and the build would fail. That is to say a non-const object can safely be passed as a const argument, but a const object cannot be passed as a non-const argument, because the function is not "promising" not to modify the object. You can force a const into a non-const argument by a const_cast as in:
const std::string temp2("hello") ;
print( const_cast<std::string&>(temp2) ) ; // hello
But if print() were to attempt to modify the object in this case the results are undefined, so consider the practice unsafe.
Making an argument const indicates intent, allows the compiler to issue a diagnostic if the code is attempts to modify the object or pass it via a non-const argument to some other function. It may also potentially provide the compiler with optimisation possibilities.
Because calling the function taking std::string const& requires two implicit conversions: one to std::string const, one to std::string const&; whereas calling the function taking std::string& requires merely one implicit conversion (to std::string&), so that one is preferred.
Say I have this constructor in C++:
A::A( std::string const& name,
std::string const& type,
std::vector<B> const& b_vec,
bool unique )
: _name(name), _type(type), _b_vec(b_vec), _unique(unique)
{ };
I would like to overload this constructor for the case where the arguments are rvalues (I want to use move semantics there).
A::A( std::string && name,
std::string && type,
std::vector<B> && b_vec,
bool unique )
: _name(name), _type(type), _b_vec(b_vec), _unique(unique)
{ };
The above one works fine when all of the arguments are rvalues, but suppose if only some of them are is in the next example:
// create some lvalues somehow
std::string name = "stack overflow";
std::vector<B> vec = { ... }; // implementation of B's constructot is not important
// call a mixed constructor
A new_A_instance(name, "cool-website", vec, true);
it is to my understanding that since 'const&' cannot bind to '&&' but '&&' can bind to 'const&' the first (non-move) constructor would be used.
This seems sub-optimal, since two of the four arguments could be moved (because they are rvalue) instead of being copied (as is the case in the first constructor).
So I could overload the operator for this specific case, but one could easily image a case where other arguments are rvalue and others are agin lvalue. Should I overload the constructor for each of these cases? This would combinatorily lead to very much overloads as the number of arguments increases...
I kind-of feel there is a better solution (perhaps using templates, but my template knowledge is shamefully low).
Note: this problem isn't tied to overloading pass-by-ref functions to move functions per-se, but I found this a good example (especially since the overloads don't feel very different). Also note that I just used constructors as an example, but the overloaded function can be anything.
Pass by value, this is what move semantics are for:
A::A(std::string name, std::string type, std::vector<B> b_vec, bool unique )
: _name(std::move(name)), _type(std::move(type)), _b_vec(std::move(b_vec)),
_unique(unique)
{ };
This has the expected behaviour in every case. Passing a temporary by value allows the compiler to perform copy elision, which it pretty much always does.
Note that in your second code, copies are made, since you don't use std::move. Please realize that when you write
void foo(bar&& x)
{
...
}
then in the body of foo, x is a lvalue. Objects with names are always lvalues. Inside this body, you must use std::move(x) if you intend to pass x as a rvalue.