I was trying to evaluate how rvalue references effect the design of the class. Say I have an existing class as shown below
class X
{
string internal;
public:
void set_data(const char* s)
{
internal = s;
}
..
..
..
//other stuff
};
This class is used by another module like this:
//another module
{
string configvalue;
X x;
//read configvalue from a file and call set
...
x.set_data(configvalue.c_str());
//use x to do some magic
..
...
}
with rvalue references in place will it be better to provide another member function like so
class X
{
...
...
....
void set_data(string s)
{
internal = std::move(s);
}
};
This will allow the clients of this class to use move semantics and prevent one set of allocate/copy operations per use. This is a highly concocted example but does the same principle apply to all class designs without breaking the 'minimal interface' paradigm.
Anybody insights on this matter are greatly appreciated?
Yes, adding the string overload as you suggest is a good idea. Even without rvalue references such an overload would be a good idea. Otherwise, given a std::string s, to use it would have to:
x.set_data(s.c_str());
whereas
x.set_data(s);
is so much more intuitive (and even slightly more efficient) for the clients of X.
As another option, you could add these two overloads:
void set_data(const string& s) {internal = s;}
void set_data(string&& s) {internal = std::move(s);}
This is roughly equivalent to the single overload you correctly suggested. There is a very slight performance advantage for the two-overload solution. The single-overload solution will cost an extra string move construction when the argument passed is an xvalue (an lvalue that has been cast with std::move). But the move constructor of std::string should be really fast, so this should not be a big deal. I mention it only in the spirit of full disclosure.
If set_data has more than one parameter, the "by-value" approach becomes much more attractive. For example consider the case where you need to pass in two strings. Your choices are:
Solution 1
void set_data(string s1, string s2);
Solution 2
void set_data(const string& s1, const string& s2);
void set_data( string&& s1, const string& s2);
void set_data(const string& s1, string&& s2);
void set_data( string&& s1, string&& s2);
As you can quickly see, Solution 2 scales poorly with the number of parameters.
Finally, in no circumstance should you attempt to apply both solutions to the same type:
Don't do this!
void set_data(string s) {internal = std::move(s);}
void set_data(const string& s) {internal = s;}
void set_data(string&& s) {internal = std::move(s);}
This set of overloads will be ambiguous. Just as in C++03 the following two overloads are ambiguous:
void set_data(string s) {internal = std::move(s);}
void set_data(const string& s) {internal = s;}
Never overload by-value with reference, either lvalue reference nor rvalue reference.
I don't see a reason to have both void set_data(const char* s) and void set_data(string s) as part of the interface. This will create an ambiguity and is prone to side-effects. Moreover, you still pass the argument by value in call to set_data(string s). Instead I would suggest defining the 2 following funcs:
void set_data(const string &s);
void set_data(string &&s);
This way you can have 2 implementations, first will deep copy your string and second one can steal the internals of the string since it's an rvalue (make sure to leave it in a defined state so the destructor will be able to destroy it without problem - for details see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html#Move_Semantics).
The second version will be invoked automatically either on rvalue string argument or if argument is forced to rvalue, for example by std::move.
If you want to have a by-value option as well you can use the rvalue version of this API combined with string copy constructor: set_data(string(str)).
Related
I just started working with c++11 r-values. I read some tutorials, but I haven't found the answer.
What is the best way (the most efficient way) to set a class variable? Is below code correct or not? (let's assume std::string has defined move constructor and assignment operator).
class StringWrapper
{
private:
std::string str_;
public:
StringWrapper() : str_("") {}
void setString1(std::string&& str) {
str_ = std::move(str);
}
void setString2(const std::string& str) {
str_ = std::move(str);
}
// other possibility?
};
int main() {
std::string myText("text");
StringWrapper x1, x2;
x1.setString?("text"); // I guess here should be setString1
x2.setString?(myText); // I guess here should be setString2
}
I know that compiler can optimize my code and/or I can use overload functions. I'd like to only know what is the best way.
Herb Sutter's advice on this is to start with the standard C++98 approach:
void setString(const std::string& str) {
str_ = str;
}
And if you need to optimize for rvalues add an overload that takes an rvalue reference:
void setString(std::string&& str) noexcept {
str_ = std::move(str);
}
Note that most implementations of std::string use the small string optimization so that if your strings are small a move is the same as a copy anyway and you wouldn't get any benefit.
It is tempting to use pass-by-value and then move (as in Adam Hunyadi's answer) to avoid having to write multiple overloads. But Herb pointed out that it does not re-use any existing capacity of str_. If you call it multiple times with lvalues it will allocate a new string each time. If you have a const std::string& overload then it can re-use existing capacity and avoid allocations.
If you are really clever you can use a templated setter that uses perfect forwarding but to get it completely correct is actually quite complicated.
Compiler designers are clever folk. Use the crystal clear and therefore maintainable
void setString(const std::string& str) {
str_ = str;
}
and let the compiler worry about optimisations. Pretty please, with sugar on top.
Better still, don't masquerade code as being encapsulated. If you intend to provide such a method, then why not simply make str_ public? (Unless you intend to make other adjustments to your object if the member changes.)
Finally, why don't you like the default constructor of std::string? Ditch str_("").
The version with rvalue reference would not normally bind to an lvalue (in your case, mytext), you would have to move it, and therefore construct the object twice, leaving you with a dangerous object. A const lvalue reference should be slower when constructing from an rvalue, because it would do the same thing again: construct -> move -> move construct.
The compiler could possibly optimize the overhead away though.
Your best bet would actually be:
void setString(std::string str)
{
str_ = std::move(str);
}
The compiler here is suprisingly guaranteed to deduce the type of the argument and call the copy constructor for lvalues and the move constructor for rvalues.
Update:
Chris Dew pointed out that constructing and move assigning a string is actually more expensive than copy constructing. I am now convinced that using a const& argument is the better option. :D
You might probably use templatized setString and forwarding references:
class StringWrapper
{
private:
std::string str_;
public:
template<typename T>
void setString(T&& str) {
str_ = std::forward<T>(str);
}
};
Does it make sense to overload set(/*args*/) with move?:
//example:
class Person {
private:
//properties
std::string name;
std::string address;
std::string favmovie;
public:
//set without operator=
void set(const std::string& name, const std::string& address, const std::string& favmovie) {
this->name = name;
this->address = address;
this->favmovie = favmovie;
return;
}
//set without operator=
void set(std::string&& name, std::string&& address, std::string&& favmovie) {
this->name = std::move(name);
this->address = std::move(address);
this->favmovie = std::move(favmovie);
return;
}
Person(const std::string& name, const std::string& address, const std::string& favmovie)
: name(name), address(address), favmovie(favmovie) {
}
Person(std::string&& name, std::string&& address, std::string&& favmovie)
: name(std::move(name)), address(std::move(address)), favmovie(std::move(favmovie)) {
}
};
It feels like copy and paste with a little bit of editing but I'm doing this to every function or method with congruent purposes I have so far to make them high-performing. But is this a good practice?
#M.M's answer has the disadvantage that storage will be reallocated every time set is called, while passing the arguments by reference will allow the data to simply be copy-assigned without reallocation whenever their capacity is already sufficient to hold the data passed in via the arguments. See Herb Sutter's CppCon talk for more details. So I would recommend
void set(const std::string &name, const std::string &adress, const std::string &favmovie)
{
this->name = name;
this->adress = adress;
this->favmovie = favmovie;
}
The constructors could use the clever pass-by-value trick, but unless it is really low level code that gets beat on a lot the optimization there is likely not worth it, IMO.
This is where you use pass-by-value:
void set(std::string name, std::string adress, std::string favmovie)
{
this->name = std::move(name);
this->adress = std::move(adress);
this->favmovie = std::move(favmovie);
}
Then if the argument is an rvalue it will be moved into the parameter than then moved into this - two moves, no copies. Whereas with the const lvalue reference version, there is always a copy. For the case of an lvalue argument there is 1 copy in both cases (and 1 extra move in the value version, but moves are cheap and may be elided anyway).
If the function's purpose itself does not benefit from move semantics, and the function would not benefit from having a reference to a mutable parameter, there is no reason to have an overloaded version that takes an rvalue reference as a parameter.
For the simple function shown here, the answer is obviously no. But it wouldn't be too hard to think of an example where the function would store its parameter someplace. In that case, having an overloaded version that takes advantage of move semantics, instead of having to copy-construct the object for the purpose of storing it, would have obvious benefits.
It all comes down to what the function needs its parameter for.
Recently I learned about function's reference qualifiers, e.g.
struct foo
{
void bar() {}
void bar1() & {}
void bar2() && {}
};
Where I might need this feature, is there any real use case for this language feature ?
Where I might need this feature, is there any real use case for this language feature ?
The example you show is pretty useless, it's more useful when you have an overloaded function, one version that operates on lvalues and one that operates on rvalues.
Consider a type a bit like std::stringstream that owns a string and returns it by value. If the object is an rvalue, it can move the string instead of copying it.
class StringBuilder
{
public:
std::string get() const& { return m_str; }
std::string get() && { return std::move(m_str); }
private:
std::string m_str;
};
This means when you return a StringBuilder from a function and want to get the string out of it, you don't need a copy:
std::string s = buildString().get();
More generally, given a function f(const X&) if it would be useful to overload it with f(X&&), then given a member function X::f() it might be useful to change it to X::f() const& and overload it with X::f()&&
There are basically two uses:
To provide an optimized overload, for example to move a member out of a temporary object instead of having to copy it.
Prevent misuse of an API. For example, no one would expect
int a = 1 += 2;
to work and this would also cause a compile error. However
string b = string("foo") += "bar";
is legal if operator += is declared as
string & operator += (string const & o);
as is usually the case. Also this has the nasty side-effect of providing an lvalue-reference to your rvalue. Bad idea. This can easily be prevented by declaring the operator as
string & operator += (string const & o) &;
I have a class like that.I want to use reference for string but it doesnt work.How can i use string& ?
#include <string>
using namespace std;
class T
{
public:
T(string& s);
private:
string s;
};
T::T(string& s)
{
this->s = s;
}
int main(void)
{
T t("Test Object");
return 0;
}
Error : 'T::T(std::string &)' : cannot convert parameter 1 from 'const char [12]' to 'std::string &'
Use const :
class T
{
public:
T(const string& ss);
private:
string s;
};
T::T(const string& ss) : s(ss)
{
}
"Test Object" will be constructed as a const string before passing to T's constructor, so the constructor has to accept a const string.
You're not passing in a std::string. As it says, it can't convert from const char array to string-ref. Change the constructor to take a const-ref and it'll work. This is because a temporary string will have to be created from the char-array, and you can only make const-references to temporaries to stop you getting confused (if you modify the temporary, the changes are just going to be discarded, so the language stops you from doing that).
In C++98/03, if you have a class that is not cheap to copy (e.g. an int or a double are cheap to copy, a std::string isn't, since its copy can involve allocating new heap memory, copying characters from source to destination, etc.), then the rule is to pass by const reference const std::string&:
class T
{
public:
T(const string& s); // <--- const string&
private:
string m_s;
};
And then in constructor do:
T::T(const string& s)
: m_s(s)
{}
However, in C++11, where move semantics is available, the new rule seems to be: if you need a copy (and the object is cheap to move, as it normally should be), pass by value and move from the value:
T::T(string s) // pass by value
: m_s( std::move(s) ) // and move from the value
{}
(The optimal thing would be to offer a couple of overloads, passing by const & and passing by value, but probably this is not necessarily in all applications, but only when you need to squeeze performance.)
Note that when you don't need a copy, and just need to observe the parameter, the usual C++98/03 pass by const & rule is still valid.
The constructor you defined takes a std::string by reference:
T::T(std::string& s)
{
this->s = s;
}
thus the most straightforward thing to do would be to create a std::string object, that will be passed to this constructor:
std::string s("Test Object");
T t(s);
But since your constructor doesn't change the std::string you pass to it (it is just used to set the value of T's data member) you should pass const reference: T::T(const string& s). Also instead of letting the data member s being constructed and assigning another string into it later, it would be better if you construct this member directly within an initialization list:
T::T(const std::string& str) : s(str) { }
References need to be intialized using the initialiser-list of the constructor. Change your constructor to:
T::T(string& s) : s(s)
{
}
Additionally define your member s as std::string& s to be able to take a reference.
Maybe change the name of s to avoid ambiguities.
See this entry on SO.
Look at the following code. What is wrong with it? The compiler gives the this error:
In copy constructor person::person(person&)':
No matching function for call toperson::copy(char*&, char*&)'
candidates are: void person::copy(char*&, const char*&) "
Here is the code:
class person
{
public:
person();
person(person &);
private:
void copy(char*&,const char*&);
char* name, *fathername,* address;
};
void person::copy( char*& n, const char*& p)
{
int result;
result=strcmp(n,p);
if(result!=0)
{
n=new char[strlen(p)+1];
strcpy(n,p);
n[strlen(p)]='\0';
}
}
person::person(person &object)
{
copy(name,object.name);
copy(fathername,object.fathername);
copy(address, object.address);
}
From the answers to this Question what I understood up until now is given by:
the compiler does not allow to convert a reference to a constant reference because references are already constant. They can't point to a different memory location like pointer. Am I right?
Wouldn't this be nicer?
class person
{
private:
std::string name;
std::string fathername
std::string address;
};
// constructor and copy constructor autogenerated!
It's more "C++" this way ;).
Unless you are planning on changing the pointers you should not pass the references to the pointers:
Change:
void person::copy( char*& n, const char*& p)
to
void person::copy( char* n, const char* p)
This is because p is a reference to a particular type.
The object you passed is not the exact type and becuase it is a reference their is no way to convert it.
The change I suggested above allows for a "pointer to const char" (p) thus allowing read only access to elements via 'p'. Now a "pointer to char" allows read/write access to the data so converting this to "pointer to const char" is allowed because we are just limiting the allowed behavior.
There are a whole set of other problems with the code you posted.
Do you want us to list them?
I don't do NOW. I do on my schedule.
Problems:
1: You leak on each call to copy:
if(result!=0)
{
n=new char[strlen(p)+1]; // What happned to the old n?
2: The default assignment operator is used.
person a;
person b;
a = b; // a.name == b.name etc all point at the same memory location.
// Though because you do not delete anything in the destructor
// it is technically not an issue yet.
3: You done delete the allocated members in the destructor.
{
person a;
} // A destructor called. You leak all the member here.
4: strcpy() already copies the terminating '\0' character.
5: if the call to new throws an exception. You will leak memory.
copy(name,object.name);
copy(fathername,object.fathername); // If new throws in here.
// Then the this.name will be leaked.
Doing this correctly using C-String is so hard that even a C++ expert would have problems doing this correctly. Thats why C++ experts would use std::string rather than a C-String. If you must use C-Strings then you should wrap your C-String in another class to protect it from probelms with exceptions.
Change
person::person(person &object)
to
person::person(const person &object)
for starters...
The compiler is telling you the problem - change your signature to accept 2 char* pointers (rather than 1 const char*) and it should compile.
The issue is really due ot the use of the reference - if you had created a copy method that simply took 2 char* pointers (not references) then the compiler will automatically recognise the conversion from char* to const char* and use the method. As you only have a method that accepts a reference to a different type it cannot do that automatically.
I'm feeling generous, so here is a corrected version of your code:
class person
{
public:
person();
person(const person &);
~person();
private:
void copy(char*&, // Do you understand purpose of '&' here?
const char*);
char* name;
char* fathername;
char* address;
};
person::person()
: name(NULL),
fathername(NULL),
address(NULL)
{
}
person::~person()
{
delete[] name;
delete[] fathername;
delete[] address;
}
void person::copy( char*& n, // The '&' is required because the contents of `n` are changed.
const char* p)
{
delete[] n;
n = NULL; // Here is one place where contents of `n` are changed.
if (p)
{
n = new char [strlen(p) + sizeof('\0')]; // Another content changing location.
strcpy(n, p);
n[strlen(p)]='\0';
}
}
person::person(const person& object)
{
copy(name,object.name);
copy(fathername,object.fathername);
copy(address, object.address);
}
Can you identify the flaws or safety items still lurking?
As others are saying, you shouldn't pass the char pointer by reference if you are not going to modify it.
The problem is that the reference is non-const and therefore doesn't bind to temporaries. Therefore the passed variable's type must match exactly. Near matches that would involve an implicit cast are not acceptable, because the result of an implicit cast is a temporary.
Const references, on the other hand, can be bound to temporaries.
void non_constant(int&);
void constant(const int&);
int main()
{
int i = 0;
unsigned u = 0;
non_constant(i);
//non_constant(u); //ERROR: not an int
//non_constant(10); //ERROR: literals are temporaries
constant(i);
constant(u); //OK, unsigned implicitly cast to int
constant(10); //OK, literals bind to const references
}
So, if you wanted badly to keep the reference in the argument:
void person::copy( char*& n, const char* const& p)
This is VERY (!) poor design. This code is buggy and VERY (!) hard to understand and maintain.
This question is continuation for this question: C++ classes , Object oriented programming.
Now you struggling with symptoms, not with real problem. And real problem is to think in C++ terms not in C (if you want to became C++ object-oriented programmer).
Valid C++ code (C++, not C with classes) here:
#include <string>
class person
{
public:
person();
private:
std::string name, fathername, address;
};
Thats all. All other things (including copy contstructor) C++ compiler generates for you (as well effective as you own manual implementation)! This much simpler, much clearer, easier to maintain and understand, and first of all: bug free;). And this is true C++ code.
Others have already correctly answered your question, but it seems you didn't understand it so far, so I will try to make it as clear as possible for you.
void person::copy( char*& n, const char*& p)
This function expects as the second argument a non-const reference to a const pointer (and not a const reference to a pointer as you might think!).
When you try to call this function passing as the second argument a pointer (not a const pointer) the compiler is unable to create a reference for it, simply because it expects to bind a reference to a const pointer and it is not allowed to implicitly cast the pointer to a const pointer, since non-const references may not bind to rvalues (temporary values).
If you want the function to expect a const reference to a const pointer, you have to change its signature as shown below:
void person::copy( char*& n, const char* const& p)
Here it is important to understant that the compiler implicitly casts the provided pointer to a const pointer before binding the reference, which is allowed in this case, since a const reference may bind both to rvalues and lvalues.
Similarly, if you want the function to expect a const reference to a pointer, which was probably your original intention, then the signature should be as shown bellow:
void person::copy( char*& n, char* const& p)
Here the compiler doesn't have to implicitly cast anything, since the provided argument already matches the type the reference expects to bind.
I hope I made it clear and detailed for you and that you correctly understood what caused the problem, which is indeed important, but nonetheless, I would strongly advice you not to write your code like this, but instead to follow the suggestions given by the others.
Others pointed that you should replace reference with pointer.
Here are a few other but related comments:
If you define copy constructor, define assignment operator too. They should go along together, in most cases.
It's a good practice to declare single argument constructor as explicit.
Naming objects as object is a bad convention and may lead to confusions. It's abvious every instance of type person is an object. Use more meaningful names, like person(const person& other); or person(const person& rhs); // after right-hand-side
Use std::string. If you are programming in C++, there is no
rational reason to not to use std::string and juggle C-strings instead.
Finally, take care of exception safety, follow best practices like copying oprations implemented in terms of non-throwing swap operation. See article Exception-Safe Class Design, Part 1: Copy Assignment by Herb Sutter