Can I have class/struct with rvalue field in c++11?
Like this one:
template<typename T>
struct RvalueTest{
RvalueTest(T&& value) : value( std::forward<T>(value) ){}
T&& value;
};
Because in the following test:
class Widget {
public:
Widget(){std::cout << "Widget ctor " << std::endl; }
Widget(int h) : h(h){std::cout << "Widget ctor param " << std::endl; }
Widget(const Widget&) { std::cout << "Widget copy ctor " << std::endl; }
Widget(Widget&&) { std::cout << "Widget move ctor " << std::endl; } // added this
template<typename T>
Widget(const T&) { std::cout << "Generalized Widget copy ctor " << std::endl; }
template<typename T>
Widget(T&&) { std::cout << "Universal Widget ctor " << std::endl; }
int h;
};
RvalueTest<Widget> r(Widget(12));
std::cout << r.value.h;
I got some trash value at output (with -O2):
http://coliru.stacked-crooked.com/a/7d7bada1dacf5352
Widget ctor param
4203470
And right value with -O0:
http://coliru.stacked-crooked.com/a/f29a8469ec179046
Widget ctor param
12
WHY???
P.S. What I try to achive is a single ctor call, without any additional move/copy constructors.
UPDATED
It compiles ok with clang http://coliru.stacked-crooked.com/a/6a92105f5f85b943
GCC bug? Or it works as it should ?
The problem is not specific to rvalue references. You will see the same odd behaviour, if you replace T&& by const T&.
template<typename T>
struct ReferenceTest{
ReferenceTest(const T& value) : value(value){}
const T& value;
};
The problem is, that you are storing a reference to a temporary object. The temporary object is automatically destroyed at the end of the statement it was created, but you try to access it later via the reference.
RvalueTest<Widget> r(Widget(12)); // pass reference to temporary object to constructor
std::cout << r.value.h; // invalid reference
It is possible to use rvalue references as member variables. However, you have to be careful if doing so. An example from the standard library is std::forward_as_tuple.
auto t = std::forward_as_tuple(42);
// t has type std::tuple<int&&>
Regarding the question how to avoid the copy or move construction: Don't use rvalue reference member variables. Here are two approaches, you can use instead:
std::unique_ptr: If copying or moving a Widget is too expensive, you can use a smart pointer:
template <typename T>
struct Test
{
std::unique_ptr<T> _value;
explicit Test(std::unique_ptr<T> value) : _value{std::move(value)} { }
};
Test<Widget> t{std::make_unique<Widget>(12)};
std::cout << t._value->h;
The second approach creates the Widget in-place. That's what the different emplace functions of the standard containers do, e.g. std::vector::emplace_back.
template <typename T>
struct Test
{
T _value;
template <typename ...Args>
explicit Test(Args&&... args) : _value{std::forward<Args>(args)...} { }
};
Test<Widget> t{12};
std::cout << t._value.h;
Related
I have a generic class, where two of the generics are going to be identical 90% of the time, but on the off chance that they are separate types, I need to perform member initialization a little bit differently.
template<typename S, typename T = S>
class MyClass {
MyClass(const Options<S> & opts) {
if (std::is_same<S,T>::value) {
thing.reset(new T(opts.get_s_thing())); // 90% of time, use type S in args to initialize thing
} else {
thing.reset(new T()); // Special case, types differ, so create a new one
}
//
// Other constructor things...
//
}
std::shared_ptr<T> thing;
};
Only thing is, since type-checking is done at compile time, the thing.reset(opts.get_s_thing()); line throws an error if I create MyClass with an explicitly different type for T. But because the types differ, that line would never get called anyways. Is there some preprocessor directive I can use here? What strategy should I take?
Changing the constructor signature in MyClass is not an option, since this is part of a VERY large codebase. T is a new thing, so I have more freedom in deciding the "standard interface" for Ts.
I would separate the two constructors. And take the rest of the current constructor code apart, e.g. to an initialize function.
[c++11] Using enable_if. [Demo]
template <typename S, typename T = S>
struct MyClass {
void initialize() {
std::cout << "initialize\n";
//
// Other constructor things...
//
}
template <typename U = S>
MyClass(const Options<typename std::enable_if<std::is_same<U,T>::value, U>::type>& opts) {
std::cout << "Same same, ";
thing.reset(new S(opts.get_s_thing()));
initialize();
}
template <typename U = S>
MyClass(const Options<typename std::enable_if<not std::is_same<U,T>::value, U>::type>& opts) {
std::cout << "But different, ";
thing.reset(new T());
initialize();
}
std::shared_ptr<T> thing;
};
[c++20] Using a requires clause. [Demo]
template <typename S, typename T = S>
struct MyClass {
void initialize()
{
std::cout << "initialize\n";
//
// Other constructor things...
//
}
MyClass(const Options<S>& opts) requires std::is_same<S,T>::value {
std::cout << "Same same, ";
thing.reset(new S(opts.get_s_thing()));
initialize();
}
MyClass(const Options<S>& opts) {
std::cout << "But different, ";
thing.reset(new T());
initialize();
}
std::shared_ptr<T> thing;
};
Other options if you want to keep the current signature completely unchanged are:
[c++11] Using partial specializations of a helper class. [Demo]
template <typename U, typename V, bool ConstructorsAreEqual>
struct MyThingInitializer;
template <typename U, typename V>
struct MyThingInitializer<U, V, true> {
static void initialize(const Options<U>& opts, std::shared_ptr<V> thing) {
std::cout << "Same same\n";
thing.reset(new V(opts.get_s_thing()));
}
};
template <typename U, typename V>
struct MyThingInitializer<U, V, false> {
static void initialize(const Options<U>&, std::shared_ptr<V> thing) {
std::cout << "But different\n";
thing.reset(new V());
}
};
MyClass(const Options<S>& opts) {
MyThingInitializer<S, T, std::is_same<S, T>::value>::initialize(opts, thing);
//
// Other constructor things...
//
}
[c++20] Using if constexpr. [Demo]
MyClass(const Options<S>& opts) {
if constexpr(std::is_same<S,T>::value)
{
std::cout << "Same same\n";
thing.reset(new S(opts.get_s_thing()));
}
else
{
std::cout << "But different";
thing.reset(new T());
}
//
// Other constructor things...
//
}
In another order of things, it's not recommended to use new with smart pointers. You may want to change your lines:
thing.reset(new S(opts.get_s_thing()));
thing.reset(new T());
for:
std::make_shared<U>(opts.get_s_thing()).swap(thing);
std::make_shared<T>().swap(thing);
I'm writing a test program about c++ type erasure, the code is put on the end.
when I run program , the test case 2 output as follow:
A default cstr...0x7ffe0fe5158f
obj_:0x7ffe0fe5158f objaaa 0x7ffe0fe5158f
Print A 0x7ffe0fe5158f
my machine: Linux x86-64, gcc 4.8
In my opinion, "Object obj2(a2);" makes a class Model by lvalue reference, so it should call A's copy constructor,
but actually it did not work, it makes me confused.
someone can give a explanation, thank you in advance.
the program is list as follow:
#include <memory>
#include <iostream>
class Object {
public:
template <typename T>
Object(T&& obj) : object_(std::make_shared<Model<T>>(std::forward<T>(obj))) {
}
void PrintName() {
object_->PrintName();
}
private:
class Concept {
public:
virtual void PrintName() = 0;
};
template <typename T>
class Model : public Concept {
public:
Model(T&& obj) : obj_(std::forward<T>(obj)) {
std::cout << "obj_:" << std::addressof(obj_) <<" objaaa " << std::addressof(obj) << std::endl;
}
void PrintName() {
obj_.PrintName();
}
private:
T obj_;
};
private:
std::shared_ptr<Concept> object_;
};
class A {
public:
A(A& a) {
std::cout<< "A copy cstr...a" << this << std::endl;
}
A(A&& a) {
std::cout << "A move cstr...." <<this<< std::endl;
}
A() {
std::cout << "A default cstr..." <<this<< std::endl;
}
void PrintName() {
std::cout << "Print A " << this << std::endl;
}
};
int main(void)
{
// test case 1
Object obj{A()};
obj.PrintName();
// test case 2
A a2;
Object obj2(a2);
obj2.PrintName();
return 0;
}
In Object obj2(a2);, no copy is made. T in the constructor of Object is deduced to be A&, so it instantiates Model<A&>, which stores a reference to the original a2 object as its obj_ member.
Observe that in your debug output, a2's constructor, Model's constructor and PrintName all print the same address. You can further confirm that this address is in fact &a2.
I have implemented a custom container (same vein as std::vector) and I am trying to make it so that its 'push_back' function would use leverage on move semantics to avoid creating a copy of whatever is being pushed back - specially when the object to be pushed into the container is returned by an external function.
After reading quite a bit about move semantics and custom containers, I still can't find why my approach is still generating a copy instead of just moving the passed object into the container's inner dynamic array.
Here is a simplified version of my container looks like:
template<class T>
class Constructor
{
private:
size_t size = 0;
size_t cap = 0;
T *data = nullptr;
public:
Constructor()
{
cap = 1;
size = 0;
data = static_cast<T*>(malloc(cap * sizeof(T)));
}
~Constructor()
{ delete[] data; }
template<typename U>
void push_back(U &&value)
{
if (size + 1 >= cap)
{
size_t new_cap = (cap * 2);
T* new_data = static_cast<T*>(malloc(new_cap * sizeof(T)));
memmove(new_data, data, (size) * sizeof(T));
for (size_t i = 0; i<cap; i++)
{
data[i].~T();
}
delete[] data;
cap = new_cap;
data = new_data;
new(data + size) T(std::forward<U>(value));
}
else
{
new(data + size) T(std::forward<U>(value));
}
++size;
}
const T& operator[](const size_t index) const //access [] overloading
{
return data[index];
}
};
Here is a custom class that will print messages when its instances are created, copied or moved, in order to help debugging:
class MyClass
{
size_t id;
public:
MyClass(const size_t new_id)
{
id = new_id;
std::cout << "new instance with id " << id << std::endl;
}
MyClass(const MyClass &passedEntity)
{
id = passedEntity.id;
std::cout << "copied instance" << std::endl;
}
MyClass(MyClass &&passedEntity)
{
id = passedEntity.id;
std::cout << "moved instance" << std::endl;
}
void printID() const
{
std::cout << "this instance's id is " << id << std::endl;
}
};
And here is the external function:
MyClass &foo(MyClass &passed)
{
return passed;
}
Lastly, here is the main function that runs a test case using the above function and classes to show the problem:
int main()
{
MyClass a(33);
std::cout << std::endl;
std::cout << "Using my custom container: " << std::endl;
Constructor<MyClass> myContainer;
myContainer.push_back(foo(a));
myContainer[0].printID();
std::cout << std::endl;
std::cout << "Using dinamic array: " << std::endl;
MyClass *dinArray = static_cast<MyClass*>(malloc(1 * sizeof(MyClass)));
dinArray = new(dinArray + 1) MyClass(std::forward<MyClass>(foo(a)));
dinArray[0].printID();
std::cout << std::endl;
system("Pause");
return 0;
}
The output is:
new instance with id 33
Using my custom container:
copied instance
this instance's id is 33
Using dinamic array:
moved instance
this instance's id is 33
As it can be seen, if the instance of MyClass is put directly into a dynamic array, then just the move conmstructor is called and not the copy constructor. However, if I push_back the yClass instance into an instance of Container, a copy constructor is still being called.
Could someone help me understand what exactly am I doing wrong here? How could I make it so that elements are pushed into the container without generating a copy?
When you call this line
myContainer.push_back(foo(a));
L-value is passed into push_back method, and now read about using std::forward - http://www.cplusplus.com/reference/utility/forward/,
Returns an rvalue reference to arg if arg is not an lvalue reference.
If arg is an lvalue reference, the function returns arg without modifying its type.
and in your push_back you call
new(data + size) T(std::forward<U>(value));
but value was passed as L-value, and only constructor MyClass(const MyClass &passedEntity) can be invoked.
If you want a object to be moved, you can write
myContainer.push_back(std::move(a)); // cast to R-reference
EDIT
You should not use move in your push_back function, below is simple example.
Suppose you have class like this:
struct Foo {
int i;
Foo (int i = 0) : i(i) {
}
~Foo () {
}
Foo (const Foo& ) {
}
Foo& operator=(const Foo&) {
return *this;
}
Foo (Foo&& f)
{
i = f.i;
f.i = 0; // this is important
}
Foo& operator=(Foo&& f)
{
i = f.i;
f.i = 0; // this is important
return *this;
}
};
we have also 2 functions
template<class T>
void process1 (const T& ) {
cout << "process1" << endl;
}
template<class T>
void process (T&& obj) {
cout << "process2" << endl;
T newObj = forward<T>(obj);
}
and bars functions are counterparts of your push_back method.
template <typename T>
void bar1 (T&& value) {
process (move(value)); // you use move in your push_back method
}
template <typename T>
void bar2 (T&& value) {
process (forward<T>(value));
}
now we must consider 4 cases:
[1] pass L-value, version with forward
Foo f(20);
bar2 (f);
cout << (f.i) << endl; // 20
[2] pass R-value, version with forward
Foo f(20);
bar2 (move(f));
cout << (f.i) << endl; // 0, it is OK bacuse we wanted to move 'f' object
[3] pass R-value, version with move
Foo f(20);
bar1 (move(f));
cout << (f.i) << endl; // 0, ok, we wanted to move 'f' object
[4] pass L-value, version with move in your push_back method
Foo f(20);
bar1 (f);
cout << (f.i) << endl; // 0 !!! is it OK ? WRONG
in last case, we passed f as L-value, but this object was moved in bar1 function, for me this is strange behavior and is incorrect.
It is not safe to perform a memmove with objects in C++.
You can find more information here Will memcpy or memmove cause problems copying classes?
If this is C++11 onwards, then what you want to use is placement new and the move constructor. (you could probably just bin the placement new though unless you really want to keep allocating memory yourself)
If this is any other version of C++ then you'll have to just accept that that either you're going to have to copy the object (like the rest of the stl) or that your object will have to implement a function like void moveTo(T& other)
Why is the following code calling a's copy constructors?
class a {
public:
a(int x) : x_(x) { std::cout << "a constructor" << std::endl; }
a(a&& a_) { std::cout << "a move constructor" << std::endl; }
a(const a& a_) { std::cout << "a copy constructor" << std::endl; }
private:
int x_;
};
class b {
public:
b(std::vector<a>&& v) : v_(std::move(v)) {}
private:
std::vector<a> v_;
};
int main() {
b s({2, 3, 4, 5, 6});
}
The output is the following:
a constructor
a constructor
a constructor
a constructor
a constructor
a copy constructor
a copy constructor
a copy constructor
a copy constructor
a copy constructor
I was expecting no copy since the vector is created in place and passed as a rvalue reference and after that moved. What's actually happening?
std::initializer_list<T> is a wrapper around an array of const T objects. (More precisely, std::initializer_list<T>::reference is const T&). Notice the const. It has to be like that, because its elements can be literals.
This means that the constructor of std::vector taking std::initializer_list has to copy the elements from the list into the vector, it cannot move them.
Would the following be an idiomatic C++11 implementation of a Scope Guard that restores a value upon scope exit?
template<typename T>
class ValueScopeGuard
{
public:
template<typename U>
ValueScopeGuard(T& value, U&& newValue):
_valuePtr(&value),
_oldValue(std::forward<U>(newValue))
{
using std::swap;
swap(*_valuePtr, _oldValue);
}
~ValueScopeGuard()
{
if(_valuePtr)
{
using std::swap;
swap(*_valuePtr, _oldValue);
}
}
// Copy
ValueScopeGuard(ValueScopeGuard const& other) = delete;
ValueScopeGuard& operator=(ValueScopeGuard const& other) = delete;
// Move
ValueScopeGuard(ValueScopeGuard&& other):
_valuePtr(nullptr)
{
swap(*this, other);
}
ValueScopeGuard& operator=(ValueScopeGuard&& other)
{
ValueScopeGuard(std::move(other)).swap(*this);
return *this;
}
private:
T* _valuePtr;
T _oldValue;
friend void swap(ValueScopeGuard& lhs, ValueScopeGuard& rhs)
{
using std::swap;
swap(lhs._valuePtr, rhs._valuePtr);
swap(lhs._oldValue, rhs._oldValue);
}
};
template<typename T, typename U>
ValueScopeGuard<T> makeValueScopeGuard(T& value, U&& newValue)
{
return {value, std::forward<U>(newValue)};
}
It could be used to temporarily change a value as follows:
int main(int argc, char* argv[])
{
// Value Type
int i = 0;
{
auto guard = makeValueScopeGuard(i, 1);
std::cout << i << std::endl; // 1
}
std::cout << i << std::endl; // 0
// Movable Type
std::unique_ptr<int> a{new int(0)};
{
auto guard = makeValueScopeGuard(a, std::unique_ptr<int>{new int(1)});
std::cout << *a << std::endl; // 1
}
std::cout << *a << std::endl; // 0
return 0;
}
Is a simple utility like this already implemented in a library somewhere? I had a look at Boost.ScopeExit, but its intended usage seems different and more complex.
Assuming makeValueScopeGuard to be implemented as :
template< typename T >
ValueScopeGuard<T> makeValueScopeGuard( T& t, T&& v )
{
return ValueScopeGuard<T>(t,std::move(v));
}
no, it is not very good implementation of scope guard, because it is going to fail when you pass l-values as the 2nd parameter :
int kk=1;
auto guard = makeValueScopeGuard(i, kk);
The second problem is that you used std::forward, when you should have used std::move.
As this question and answers show, people are usually using lambdas to implement scope guard.
Your move constructor leaves the pointer member uninitialized, so the rvalue object ends up holding a junk pointer, which it dereferences in its destructor. That's a bug. You should initialize it to nullptr and check for nullptr in the destructor.
For a type like this I would not expect move assignment to be a simple swap, I would expect the rvalue to end up not owning anything. So I would implement the move like this instead, so the rvalue ends up empty:
ValueScopeGuard& operator=(ValueScopeGuard&& other)
{
ValueScopeGuard(std::move(other)).swap(*this);
return *this;
}
The name makeValueScopeGuard isn't clear to me that it changes the value itself, I'd expect it to just copy the current value and restore it in the destructor.
As far as existing types go, the closest I can think of is the Boost I/O state savers, which do not alter the current state they just copy it and restore it.