Is there any real use case for function's reference qualifiers? - c++

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) &;

Related

What is the most efficient way to set class variable using rvalue in c++?

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

Storing const reference to an object in class

This sounds like a basic question, but I didn't find any comprehensive answer, so here it is. Consider this code snippet:
struct A {
const std::string& s;
A(const std::string& s) : s(s) {}
};
int main() {
A a("abc");
std::cout << a.s << std::endl;
return 0;
}
Demo.
As long as I understand, this is UB. String literal "abc" binds to const std::string& in constructor, creating a temporary string object. It is also bound to reference a.s, and it is destroyed once a is constructed. That is, const reference cannot chain lifetime prolongation. Dangling reference, boom. In this particular case I see no output at all on ideone.com, but anything could happen (remember velociraptors).
Ok, this one is clear. But what if this is actually our very intent: we want to store a const reference to an object? To an existing one, not to temporary? This sounds like a very natural task, yet I came up with only one (almost) natural solution to it. Accepting constructor's argument by std::reference_wrapper instead of by reference:
A(std::reference_wrapper<const std::string> r) : s(r) {}
Since std::reference_wrapper has deleted constructors from temporaries:
reference_wrapper( T&& x ) = delete;
this works just like expected. However, this is not quite elegant. Another approach I can think of is to accept forwarding reference T&& and to reject everything except const l-value strings with std::enable_if. This is even less elegant, I think.
Any other approaches?
UPD Another question: is this a legitimate usage of std::reference_wrapper, or may it be considered too specific?
I'd say the natural solution would be to do what reference_wrapper does: prevent construction from temporaries:
struct A {
const std::string& s;
A(const std::string& s) : s(s) {}
A(std::string&&) = delete;
};
You should also bear in mind that having a data member of reference type makes the class non-assignable (not even move assignment is possible) by default, and it's generally difficult to implement an assignment operator. You should consider storing a pointer instead of a reference:
struct A {
const std::string* s;
A(const std::string& s) : s(&s) {}
A(std::string&&) = delete;
};
There is a very simple class lvalue_ref provided in the lightweight and convenient "explicit" library by Andrzej Krzemieński, which solves exactly this problem with a clear intention:
struct Processor
{
Big const& _big;
explicit Processor(lvalue_ref<const Big> b) : _big(b) {}
};
const Big b {};
Processor p {b}; // ok
Processor q {Big{}}; // error (temporary)

Rvalue references and class design

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)).

Moving with lambdas

When using lambda functions, let's say that you decide to copy a variable (with the [=] notation). If you never reference that variable again, is the compiler allowed to move it into the resultant function object?
Edit: For example, I've wrote a snippet to move calls across threads. Here's a sample that does so.
extern "C" __declspec(dllexport) void parser_file_updated(Parser* p, const char* filename, int offset, int added) {
std::string file(filename);
p->make_call([=]() {
p->file_updated(std::move(file), offset, added);
});
}
But clearly, the file variable doesn't need to live past the lambda definition- and indeed, the lambda is only called once, so I moved the copy.
If you never reference that variable again, is the compiler allowed to move it into the resultant function object?
No. The only situation where the compiler is allowed to replace a copy with a move are the exact same situations where it is allowed to perform a copy elision. These situations include returning a local object by value or initializing an object with a temporary. In these cases the compiler is allowed to elide the copy by making source and target the same object. If the compiler is not able to do that for whatever reason it has to consider the source object as an rvalue with respect to overload resolution for selecting the appropriate constructor for the target object. In your case, however, file is an Lvalue and none of the cases from above apply. You would have to use an explicit move.
Unfortunately, C++11 doesn't have a syntax for a "move capture". IMHO, it's a shame. But std::bind supports this. It should be possible to combine std::bind with a lambda expression like this:
void foo(char const* p) {
string s = p;
auto fun = bind([](string const& s){
...
},move(s));
fun();
}
so that the string is moved into the function object.
If you intent to call this function only once and want to move the string out of the function object again, you can use a non-const reference:
void foo(char const* p) {
string s = p;
auto fun = bind([](string & s) {
some_other_func(move(s));
},move(s));
fun();
}
Note that, if you don't want to use bind here but let the lambda object's constructor create a copy of s, moving the string out of the function object requires the mutable keyword:
void foo(char const* p) {
string s = p;
auto fun = [=]() mutable {
// ^^^^^^^
some_other_func(move(s));
};
fun();
}
because otherwise the closure type's operator() function will be const-qualified which in turn makes s a const-qualified string.
In C++14 the lambda capture clause got a little more flexible. We can now write
void foo(char const* p) {
string s = p;
auto fun = [s=move(s)]() mutable { // #1
some_other_func(move(s)); // #2
};
fun();
}
where #1 moves the string value into the lambda object and #2 moves the string value out (depending on how some_other_func is declared exactly).
Update C++14: It is possible with initialised captures (added with this version):
[x = std::move(y)]() { /* ... */ }
Though with variadic templates I only managed to get the appropriate effect via a std::tuple and std::apply (if I missed some syntactic sugar for appreciate a hint...):
template <typename ... T>
void f(T&& ... t)
{
[x = std::tuple<T...>(std::move(t) ...)]()
{
std::apply([&](auto const& ... t) { /* ... */ }, x);
}
}
Demonstration on godbolt – note how the strings get emptied illustrating how their contents are moved into x or into the tuple.

Assign std::string from an object using operator=

I'd really like to be able to assign a std::string object from a DecoratedString object that I'm writing.
class DecoratedString
{
private:
std::string m_String;
public:
DecoratedString(const std::string& initvalue)
: m_String(initvalue)
{
}
const std::string& ToString() const
{
return m_String;
}
const std::string& operator=(const DecoratedString& rhs)
{
return rhs.ToString();
}
}
I've written a unit test to make sure this works:
void DecoratedStringTest::testAssignmentToString()
{
std::string expected("test");
DecoratedString sut(expected);
std::string actual;
actual = sut;
CPPUNIT_ASSERT_EQUAL(actual, sut.ToString());
}
However, the compiler says error: no match for 'operator=' in 'actual = sut'. It then lists the overloaded operator= options from the standard library.
Why isn't the compiler finding the operator= I defined?
EDIT:
So I guess I need a conversion operator, not an assignment operator. Huge thanks to the people that saw what I was trying to do and explained what I should do instead.
The operator = you have defined is for assigning decorated strings to other decorated strings and returning an std::string from that assignment.
What you want is a member "conversion operator" that automatically converts a decorated string to an std::string whenever required, like this:
operator std::string const &() const { return ToString(); }
That will also convert a decorated string automatically to a std::string const & whenever one is needed (i.e. when comparing to an std::string, or passing a DecoratedString to a function which takes a std::string const &).
The compiler is complaining about this line:
actual = sut;
which should be:
actual = sut.ToString();
Alternatively you should provide a cast operator to implicitly convert a DecoratedString to a std::string:
class DecoratedString
{
...
operator std::string() const
{
return ToString();
}
};
You're overloading the unary assignment operator that is intended for assigning a specific type to DecoratedString and is supposed to return a reference to DecoratedString so you can chain assignments. This operator is not designed to allow you to assign an object of DecoratedString to a different datatype, but rather so you can assign an object that isn't necessarily a DecoratedString to a DecoratedString. It also gives you a well-defined function to handle any sort of specific processing that an assignment of your class might required (deep copy, for example).
You either have to call your ToString() function or you'll have to create a conversion operator that can convert your DecoratedString into a std::string by implementing the following member function for DecoratedString:
operator std::string () const;
This might or might not be a good idea as this conversion operator will be used implicitly by the compiler and might lead to unexpected behaviour.
As an aside, another reason why your operator overload is not working is that you're trying to overload a function by its return value, which is a big no-no in C++.
You got it backwards. The operator= needs to be defined on the std::string to be able to accept your object, not the class you are assigning from, but since you are dealing with the standard library you can't do that.
An alternative would be a streaming operator (as a free function):
std::string& operator<<( std::string& out, const your_class& yc )
{
out.assign( yc.to_string());
return out;
}
your_class myobj;
std::string str;
str << myobj;
Or just define regular streaming for std::ostream and use std::stringstream.
Edit:
Yet another option, as others noted, is the type conversion operator:
your_class::operator const std::string() const;
Modern practice though tells us it's not the greatest idea (temporaries, extra copies, surprises).