Let me have a custom wrapper container. I want to use it like this:
double d = 3.14;
MyContainer<std::vector<int>> pointer = new std::vector<int>();
MyContainer<std::string> rvalue = std::string("foo");
MyContainer<int> rvalue2 = 5 + 8;
MyContainer<double> lvalue = d;
I don't want to store copies of rvalues (a reference is OK). Rvalue references allow me to do like this:
std::string string1 = "foo";
std::string string2 = "bar";
std::string&& string3 = string1 + string2;
string3 += "test";
Basically I want to extend rvalues' lifetime to my container's lifetime. However when I do this:
template<class T>
class MyContainer {
public:
MyContainer(T&& obj) : object(obj) {}
T&& object
...
};
...
MyContaier<std::string> container = MyContainer(std::string("foo"));
I get an error (cannot bind 'std::string' lvalue to 'std::string&&'). The example is slightly different, but I just want to understand a general idea. How can I avoid this?
Besides that your code has multiple typos and syntax errors, there was nothing technically preventing you from taking an rvalue reference of std::string (although your assignment/constructor call is incorrect). Keeping a T&& as a member variable doesn't work like you think it does. Storing a reference to an expired rvalue and then accessing it would be UB as soon as you reach the next sequence point.
Here's a working example with a constructor for rvalue references and lvalue references. You need an actual instance inside your object if you want to "own" it. You can't technically extend the lifetime of an expiring rvalue; you can only construct something else that reuses it (and hopefully steals some of its expensive guts). Hope this helps.
#include <utility>
#include <string>
#include <iostream>
template<class T>
class MyContainer {
public:
// makes a copy from an lvalue reference
MyContainer(const T& obj)
: object(obj) {
}
// moves from an rvalue reference
MyContainer(T&& obj)
: object(std::move(obj)) {
}
MyContainer& operator=(const T& obj) {
object = obj;
}
MyContainer& operator=(T&& obj) {
object = std::move(obj);
}
T object;
};
int main() {
MyContainer<std::string> container = std::string("foo");
std::cout << container.object;
}
Related
What would be a valid use case for a signature like this?:
T&& foo();
Or is the rvalue ref only intended for use as argument?
How would one use a function like this?
T&& t = foo(); // is this a thing? And when would t get destructed?
For a free function it doesn't make much sense to return a rvalue reference. If it is a non-static local object then you never want to return a reference or pointer to it because it will be destroyed after the function returns. It can possibly make sense to return a rvalue reference to an object that you passed to the function though. It really depends on the use case for if it makes sense or not.
One thing that can greatly benefit from returning an rvalue reference is a member function of a temporary object. Lets say you have
class foo
{
std::vector<int> bar;
public:
foo(int n) : bar(n) {}
std::vector<int>& get_vec() { return bar; }
};
If you do
auto vec = foo(10).get_vec();
you have to copy because get_vec returns an lvalue. If you instead use
class foo
{
std::vector<int> bar;
public:
foo(int n) : bar(n) {}
std::vector<int>& get_vec() & { return bar; }
std::vector<int>&& get_vec() && { return std::move(bar); }
};
Then vec would be able to move the vector returned by get_vec and you save yourself an expensive copy operation.
T&& t = foo(); // is this a thing? And when would t get destructed?
An rvalue reference is really similar to a lvalue reference. Think about your example like it was normal references:
T& foo();
T& t = foo(); // when is t destroyed?
The answer is that t is still valid to use as long as the object is refers to lives.
The same answer still applies to you rvalue reference example.
But... does it make sense to return an rvalue reference?
Sometimes, yes. But very rarely.
consider this:
std::vector<int> v = ...;
// type is std::tuple<std::vector<int>&&>
auto parameters = std::forward_as_tuple(std::move(v));
// fwd is a rvalue reference since std::get returns one.
// fwd is valid as long as v is.
decltype(auto) fwd = std::get<0>(std::move(parameters));
// useful for calling function in generic context without copying
consume(std::get<0>(std::move(parameters)));
So yes there are example. Here, another interesting one:
struct wrapper {
auto operator*() & -> Heavy& {
return heavy;
}
auto operator*() && -> Heavy&& {
return std::move(heavy);
}
private:
Heavy instance;
};
// by value
void use_heavy(Heavy);
// since the wrapper is a temporary, the
// Heavy contained will be a temporary too.
use_heavy(*make_wrapper());
I think a use case would be to explicitly give permission to "empty" some non-local variable. Perhaps something like this:
class Logger
{
public:
void log(const char* msg){
logs.append(msg);
}
std::vector<std::string>&& dumpLogs(){
return std::move(logs);
}
private:
std::vector<std::string> logs;
};
But I admit I made this up now, I never actually used it and it also can be done like this:
std::vector<std::string> dumpLogs(){
auto dumped_logs = logs;
return dumped_logs;
}
I was recently refreshing my C++ knowledge in operator overloading. As recommended I return a reference to *this for the operator overload of '='. But then I found one problem :-
#include <iostream>
using namespace std;
class MyClass
{
int num = 4;
public:
MyClass() = default;
MyClass(int x) : num(x) {}
void getnum(int x)
{
num = x;
}
void shownum()
{
cout << num << '\n';
}
MyClass& operator = (const MyClass& obj) // works even without const
{
this->num = obj.num;
return *this;
}
~MyClass() = default;
};
int main()
{
MyClass x, y(5), z(7);
z = MyClass(8) = y; // temporary object return reference
x.shownum();
y.shownum();
z.shownum();
}
This code doesnt result into a UB even though a temporary object MyClass(8) is involved in between which will be equated with y (fine no problem) but then it's reference will be sent to equate with z. Why doesn't the dangling reference problem occur here ? Why does the overload function work without const MyClass&' on the temporary object 'MyClass(8) ?
Temporaries live until the full expression ends. At the end the temporary goes away, but by then the assignments have all been performed and since you don't store the reference anywhere it's okay for the temporary to go away.
The first assignment MyClass(8) = y; returns a MyClass& that doesn't tell anything about being a temporary. And so it matches the parameter of the next operator=, whether that one expects a const or non-const parameter.
If you try to just assign a temporary z = MyClass(8);, you will see that it now requires the parameter to be a const reference.
Also, there are no dangling references here, because temporary objects live until the end of the full expression (usually at the ;), and no pointers or references are saved anywhere. Just copies of the num values.
I followed this tutorial to start to understand the move semantics and rvalue references in C++11.
At some point, he implements these two classes with the std::move in the move constructors explaining that
we pass the temporary to a move constructor, and it takes on new life
in the new scope. In the context where the rvalue expression was
evaluated, the temporary object really is over and done with. But in
our constructor, the object has a name; it will be alive for the
entire duration of our function. In other words, we might use the
variable other more than once in the function, and the temporary
object has a defined location that truly persists for the entire
function. It's an lvalue in the true sense of the term locator value
class MetaData
{
public:
MetaData(int size, const string& name)
: _name(name)
, _size(size)
{}
MetaData(const MetaData& other)
: _name(other._name)
, _size(other._size)
{
cout << "MetaData -- Copy Constructor" << endl;
}
MetaData(MetaData&& other)
: _name(move(other._name))
, _size(other._size)
{
cout << "MetaData -- Move Constructor" << endl;
}
~MetaData()
{
_name.clear();
}
string getName() const { return _name; }
int getSize() const { return _size; }
private:
string _name;
int _size;
};
class ArrayWrapper
{
public:
ArrayWrapper()
: _p_vals(new int[64])
, _metadata(64, "ArrayWrapper")
{}
ArrayWrapper(int n)
: _p_vals(new int[n])
, _metadata(n, "ArrayWrapper")
{}
ArrayWrapper(ArrayWrapper&& other)
: _p_vals(other._p_vals)
, _metadata(move(other._metadata))
{
cout << "ArrayWrapper -- Move Constructor" << endl;
other._p_vals = nullptr;
}
ArrayWrapper(const ArrayWrapper& other)
: _p_vals(new int[other._metadata.getSize()])
, _metadata(other._metadata)
{
cout << "ArrayWrapper -- Copy Constructor" << endl;
for (int i = 0; i < _metadata.getSize(); ++i)
_p_vals[i] = other._p_vals[i];
}
~ArrayWrapper()
{
delete[] _p_vals;
}
int* getVals() const { return _p_vals; }
MetaData getMeta() const { return _metadata; }
private:
int* _p_vals;
MetaData _metadata;
};
In the ArrayWrapper move constructor I tried to change std::move with std::forward<MetaData> and the code shows that if I call the ArrayWrapper move constructor this will call the MetaData move constructor, like the example with the std::move.
Of course if I don't use either std::move or std::forward the MetaData copy costructor will be called.
The question is, in this case, is there a difference between using std::move and std::forward? Why should I use one instead of the other?
is there a difference between using std::move and std::forward? Why should I use one instead of the other?
Yes, std::move returns an rvalue reference of its parameter, while std::forward just forwards the parameter preserving its value category.
Use move when you clearly want to convert something to an rvalue. Use forward when you don't know what you've (may be an lvalue or an rvalue) and want to perfectly forward it (preserving its l or r valueness) to something. Can I typically/always use std::forward instead of std::move? is a question you might be interested in here.
In the below snippet, bar would get exactly what the caller of foo had passed, including its value category preserved:
template <class T>
void foo(T&& t) {
bar(std::forward<T>(t));
}
Don't let T&& fool you here - t is not an rvalue reference. When it appears in a type-deducing context, T&& acquires a special meaning. When foo is instantiated, T depends on whether the argument passed is an lvalue or an rvalue. If it's an lvalue of type U, T is deduced to U&. If it's an rvalue, T is deduced to U. See this excellent article for details. You need to understand about value categories and reference collapsing to understand things better in this front.
The relevant std::forward and std::move declarations are:
template< class T >
T&& forward( typename std::remove_reference<T>::type& t );
template< class T >
typename std::remove_reference<T>::type&& move( T&& t );
For the former:
std::forward<MetaData>(other._metadata);
std::forward<MetaData> returns MetaData&&.
For the latter:
std::move(other._metadata);
//argument derived as lvalue reference due to forwarding reference
std::move<MetaData&>(other._name);
std::move<MetaData&> returns typename std::remove_reference<MetaData&>::type&&, which is MetaData&&.
So the two forms are identical for your example. However, std::move is the right choice here, as it shows our intent to unconditionally move the argument. std::forward can be used to unconditionally move, but the purpose of it is to perfect-forward its argument.
The problem
Suppose we implement a string class which represents, uhm, strings. We then want to add an operator+ which concatenates two strings, and decide to implement that via expression templates to avoid multiple allocations when doing str1 + str2 + ... + strN.
The operator will look like this:
stringbuilder<string, string> operator+(const string &a, const string &b)
stringbuilder is a template class, which in turn overloads operator+ and has an implicit string conversion operator. Pretty much the standard textbook exercise:
template<class T, class U> class stringbuilder;
template<> class stringbuilder<string, string> {
stringbuilder(const string &a, const string &b) : a(a), b(b) {};
const string &a;
const string &b;
operator string() const;
// ...
}
// recursive case similar,
// building a stringbuilder<stringbuilder<...>, string>
The above implementation works perfectly as long as someone does
string result = str1 + str2 + ... + strN;
However, it has a subtle bug. Assigning the result to a variable of the right type will make that variable hold references to all the strings that compose the expression. That means, for instance, that changing one of the strings will change the result:
void print(string);
string str1 = "foo";
string str2 = "bar";
right_type result = str1 + str2;
str1 = "fie";
print(result);
This will print fiebar, because of the str1 reference stored inside the expression template. It gets worse:
string f();
right_type result = str1 + f();
print(result); // kaboom
Now the expression template will contain a reference to a destroyed value, crashing your program straight away.
Now what's that right_type? It is of course stringbuilder<stringbuilder<...>, string>, i.e. the type the expression template magic is generating for us.
Now why would one use a hidden type like that? In fact, one doesn't use it explicitely -- but C++11's auto does!
auto result = str1 + str2 + ... + strN; // guess what's going on here?
The question
The bottom line is: it seems that this way of implementing expression templates (by storing cheap references instead of copying values or using shared pointers) gets broken as soon as one tries to store the expression template itself.
Therefore, I'd pretty much like a way of detecting if I'm building a rvalue or a lvalue, and provide different implementations of the expression template depending on whether a rvalue is built (keep references) or a lvalue is built (make copies).
Is there an estabilished design pattern to handle this situation?
The only things I was able to figure out during my research were that
One can overload member functions depending on this being an lvalue or rvalue, i.e.
class C {
void f() &;
void f() &&; // called on temporaries
}
however, it seems I can't do that on constructors as well.
In C++ one cannot really do ``type overloads'', i.e. offer multiple implementations of the same type, depending on how the type is going to be used (instances created as lvalues or rvalues).
I started this in a comment but it was a bit big for that. Then, let's make it an answer (even though it doens't really answer your question).
This is a known issue with auto. For instance, it has been discussed by Herb Sutter here and in more details by Motti Lanzkron here.
As they say, there were discussions in the committee to add operator auto to C++ to tackle this problem. The idea would be instead of (or in addition to) providing
operator string() const;
as you mentioned, one would provide
string operator auto() const;
to be used in type deduction contexts. In this case,
auto result = str1 + str2 + ... + strN;
would not deduce the type of result to be the "right type" but rather the type string because that's what operator auto() returns.
AFAICT this is not going to happen in C++14. C++17 pehaps...
Elaborating on a comment I made to the OP; example:
This only tackles the problem of assigning to either an object or binding to a reference and afterwards converting to a destination type. It is not a comprehensive fix the the problem (also see Yakk's response to my comment), but it prevents the scenario presented in the OP and makes it generally harder to write this kind of error-prone code.
Edit: It might not be possible to expand this approach for class templates (more specifically, the specialization of std::move). Macro'ing could work for this specific problem, but is obviously ugly. Overloading std::move would rely on UB.
#include <utility>
#include <cassert>
// your stringbuilder class
struct wup
{
// only use member functions with rvalue-ref-qualifier
// this way, no lvalues of this class can be used
operator int() &&
{
return 42;
}
};
// specialize `std::move` to "prevent" from converting lvalues to rvalue refs
// (make it much harder and more explicit)
namespace std
{
template<> wup&& move(wup&) noexcept
{
assert(false && "Do not use `auto` with this expression!");
}
// alternatively: no function body -> linker error
}
int main()
{
auto obj = wup{};
auto& lref = obj;
auto const& clref = wup{};
auto&& rref = wup{};
// fail because of conversion operator
int iObj = obj;
int iLref = lref;
int iClref = clref;
int iRref = rref;
int iClref_mv = std::move(clref);
// assert because of move specialization
int iObj_mv = std::move(obj);
int iLref_mv = std::move(lref);
int iRref_mv = std::move(rref);
// works
int i = wup{};
}
Just a wild idea (haven't tried it):
template<class T, class U>
class stringbuilder
{
stringbuilder(stringbuilder const &) = delete;
}
wouldn't force compilation error?
A possible approach would be using the null object pattern. While it might make your string builder bigger, it will still avoid the memory allocations.
template <>
class stringbuilder<std::string,std::string> {
std::string lhs_value;
std::string rhs_value;
const std::string& lhs;
const std::string& rhs;
stringbuilder(const std::string &lhs, const std::string &rhs)
: lhs(lhs), rhs(rhs) {}
stringbuilder(std::string&& lhs, const std::string &rhs)
: lhs_value(std::move(lhs)), lhs(lhs_value), rhs(rhs) {}
stringbuilder(const std::string& lhs, std::string&& rhs)
: rhs_value(std::move(rhs)), lhs(lhs), rhs(rhs_value) {}
stringbuilder(std::string&& lhs, std::string&& rhs)
: lhs_value(std::move(lhs)), rhs_value(std::move(rhs)),
lhs(lhs_value), rhs(rhs_value) {}
//...
If the argument to the constructor is an lvalue, then you store a reference to the real object. If the argument to the constructor is an rvalue, you can move that into an internal variable with almost no cost (move operations are cheap) and store a reference to that internal object. The rest of the code can access the reference knowing (well, at least hoping) that the string will still be alive.
The hoping part is because there is nothing blocking misuse if an lvalue is passed but the object is destroyed before the stringbuilder completes its job.
Here is another attempt at solving the issue of dangling references. It doesn't solve the issue of references to things that are modified though.
The idea is to store the temporaries into values, but to have references to lvalues (that we can expect to keep living after the ;).
// Temporary => store a copy
// Otherwise, store a reference
template <typename T>
using URefUnlessTemporary_t
= std::conditional_t<std::is_rvalue_reference<T&&>::value
, std::decay_t<T>
, T&&>
;
template <typename LHS, typename RHS>
struct StringExpression
{
StringExpression(StringExpression const&) = delete;
StringExpression(StringExpression &&) = default;
constexpr StringExpression(LHS && lhs_, RHS && rhs_)
: lhs(std::forward<LHS>(lhs_))
, rhs(std::forward<RHS>(rhs_))
{ }
explicit operator std::string() const
{
auto const len = size(*this);
std::string res;
res.reserve(len);
append(res, *this);
return res;
}
friend constexpr std::size_t size(StringExpression const& se)
{
return size(se.lhs) + size(se.rhs);
}
friend void append(std::string & s, StringExpression const& se)
{
append(s, se.lhs);
append(s, se.rhs);
}
friend std::ostream & operator<<(std::ostream & os, const StringExpression & se)
{ return os << se.lhs << se.rhs; }
private:
URefUnlessTemporary_t<LHS> lhs;
URefUnlessTemporary_t<RHS> rhs;
};
template <typename LHS, typename RHS>
StringExpression<LHS&&,RHS&&> operator+(LHS && lhs, RHS && rhs)
{
return StringExpression<LHS&&,RHS&&>{std::forward<LHS>(lhs), std::forward<RHS>(rhs) };
}
I've no doubt this could be simplified.
int main ()
{
constexpr static auto c = exp::concatenator{};
{
std::cout << "RVREF\n";
auto r = c + f() + "toto";
std::cout << r << "\n";
std::string s (r);
std::cout << s << "\n";
}
{
std::cout << "\n\nLVREF\n";
std::string str="lvref";
auto r = c + str + "toto";
std::cout << r << "\n";
std::string s (r);
std::cout << s << "\n";
}
{
std::cout << "\n\nCLVREF\n";
std::string const str="clvref";
auto r = c + str + "toto";
std::cout << r << "\n";
std::string s (r);
std::cout << s << "\n";
}
}
NB: I don't provide size(), append() nor concatenator, they aren't the points where the difficulties lie.
PS: I've used C++14 only to simplify the type traits.
Recently I have read that it makes sense when returning by value from a function to qualify the return type const for non-builtin types, e.g.:
const Result operation() {
//..do something..
return Result(..);
}
I am struggling to understand the benefits of this, once the object has been returned surely it's the callers choice to decide if the returned object should be const?
Basically, there's a slight language problem here.
std::string func() {
return "hai";
}
func().push_back('c'); // Perfectly valid, yet non-sensical
Returning const rvalues is an attempt to prevent such behaviour. However, in reality, it does way more harm than good, because now that rvalue references are here, you're just going to prevent move semantics, which sucks, and the above behaviour will probably be prevented by the judicious use of rvalue and lvalue *this overloading. Plus, you'd have to be a bit of a moron to do this anyway.
It is occasionally useful. See this example:
class I
{
public:
I(int i) : value(i) {}
void set(int i) { value = i; }
I operator+(const I& rhs) { return I(value + rhs.value); }
I& operator=(const I& rhs) { value = rhs.value; return *this; }
private:
int value;
};
int main()
{
I a(2), b(3);
(a + b) = 2; // ???
return 0;
}
Note that the value returned by operator+ would normally be considered a temporary. But it's clearly being modified. That's not exactly desired.
If you declare the return type of operator+ as const I, this will fail to compile.
There is no benefit when returning by value. It doesn't make sense.
The only difference is that it prevents people from using it as an lvalue:
class Foo
{
void bar();
};
const Foo foo();
int main()
{
foo().bar(); // Invalid
}
Last year I've discovered another surprising usecase while working on a two-way C++-to-JavaScript bindings.
It requires a combination of following conditions:
You have a copyable and movable class Base.
You have a non-copyable non-movable class Derived deriving from Base.
You really, really do not want an instance of Base inside Derived to be movable as well.
You, however, really want slicing to work for whatever reason.
All classes are actually templates and you want to use template type deduction, so you cannot really use Derived::operator const Base&() or similar tricks instead of public inheritance.
#include <cassert>
#include <iostream>
#include <string>
#include <utility>
// Simple class which can be copied and moved.
template<typename T>
struct Base {
std::string data;
};
template<typename T>
struct Derived : Base<T> {
// Complex class which derives from Base<T> so that type deduction works
// in function calls below. This class also wants to be non-copyable
// and non-movable, so we disable copy and move.
Derived() : Base<T>{"Hello World"} {}
~Derived() {
// As no move is permitted, `data` should be left untouched, right?
assert(this->data == "Hello World");
}
Derived(const Derived&) = delete;
Derived(Derived&&) = delete;
Derived& operator=(const Derived&) = delete;
Derived& operator=(Derived&&) = delete;
};
// assertion fails when the `const` below is commented, wow!
/*const*/ auto create_derived() { return Derived<int>{}; }
// Next two functions hold reference to Base<T>/Derived<T>, so there
// are definitely no copies or moves when they get `create_derived()`
// as a parameter. Temporary materializations only.
template<typename T>
void good_use_1(const Base<T> &) { std::cout << "good_use_1 runs" << std::endl; }
template<typename T>
void good_use_2(const Derived<T> &) { std::cout << "good_use_2 runs" << std::endl; }
// This function actually takes ownership of its argument. If the argument
// was a temporary Derived<T>(), move-slicing happens: Base<T>(Base<T>&&) is invoked,
// modifying Derived<T>::data.
template<typename T>
void oops_use(Base<T>) { std::cout << "bad_use runs" << std::endl; }
int main() {
good_use_1(create_derived());
good_use_2(create_derived());
oops_use(create_derived());
}
The fact that I did not specify the type argument for oops_use<> means that the compiler should be able to deduce it from argument's type, hence the requirement that Base<T> is actually a real base of Derived<T>.
An implicit conversion should happen when calling oops_use(Base<T>). For that, create_derived()'s result is materialized into a temporary Derived<T> value, which is then moved into oops_use's argument by Base<T>(Base<T>&&) move constructor. Hence, the materialized temporary is now moved-from, and the assertion fails.
We cannot delete that move constructor, because it will make Base<T> non-movable. And we cannot really prevent Base<T>&& from binding to Derived<T>&& (unless we explicitly delete Base<T>(Derived<T>&&), which should be done for all derived classes).
So, the only resolution without Base modification here is to make create_derived() return const Derived<T>, so that oops_use's argument's constructor cannot move from the materialized temporary.
I like this example because not only it compiles both with and without const without any undefined behaviour, it behaves differently with and without const, and the correct behavior actually happens with const only.