I'm trying to understand how I should use smart pointers efficiently, and I got curious about how they work together with rvalue references. How come std::make_shared (and presumably make_unique as well) uses copy semantics and not move semantics?
Here's a gtest test that showcases what I'm trying to say
#include <memory>
int dtor_calls = 0;
struct MoveSemanticsTest1 {
int data;
~MoveSemanticsTest1() { dtor_calls++; }
};
void reset_move_dtor_calls() {
dtor_calls = 0;
}
TEST(MoveSemanticsSanityTest1, SanityTests) {
reset_move_dtor_calls();
{
MoveSemanticsTest1 a = MoveSemanticsTest1();
}
EXPECT_EQ(1, dtor_calls); // <-- This passes, makes sense
reset_move_dtor_calls();
{
MoveSemanticsTest1 b = {3};
auto a = std::make_shared<MoveSemanticsTest1>(std::move(b));
}
EXPECT_EQ(1, dtor_calls); // <-- This fails, why?
reset_move_dtor_calls();
{
MoveSemanticsTest1 b = {3};
auto a = std::make_shared<MoveSemanticsTest1>(b);
}
EXPECT_EQ(2, dtor_calls); // <-- This passes, makes sense because of the copying
}
The second EXPECT_EQ fails, which hints to the moved b resource doesn't actually move the resource.
reset_move_dtor_calls();
{
MoveSemanticsTest1 b = {3}; //1
auto a = std::make_shared<MoveSemanticsTest1>(std::move(b)); //2
//3
//4
}
In 1) you create a MoveSemanticsTest1.
In 2) you create a MoveSemanticsTest1 by move construction an give it to a shared_ptr. b is in a "moved from" state, but still here.
In 3) you destroy the shared_ptr => it destroy its MoveSemanticsTest1
In 4) you destroy the MoveSemanticsTest1 b.
I count 2 call to the destructor.
Related
I was tinkering with the example given on the cppreference launder web page.
The example shown below suggest that either I misunderstood something and introduced UB or that there is a bug somewhere or that clang is to lax or too good.
In doit1(), I believe the optimization done by GCC is incorrect (the function returns 2) and does not take into account the fact that we use the placement new return value.
In doit2(), I believe the code is also legal but with GCC, no code is produced ?
In both situations, clang provides the behavior I expect. On GCC, it will depend on the optimization level. I tried GCC 12.1 but this is not the only GCC version showing this behavior.
#include <new>
struct A {
virtual A* transmogrify(int& i);
};
struct B : A {
A* transmogrify(int& i) override {
i = 2;
return new (this) A;
}
};
A* A::transmogrify(int& i) {
i = 1;
return new (this) B;
}
static_assert(sizeof(B) == sizeof(A), "");
int doit1() {
A i;
int n;
int m;
A* b_ptr = i.transmogrify(n);
// std::launder(&i)->transmogrify(m); // OK, launder is NOT redundant
// std::launder(b_ptr)->transmogrify(m); // OK, launder IS redundant
(b_ptr)->transmogrify(m); // KO, launder IS redundant, we use the return value of placment new
return m + n; // 3 expected, OK == 3, else KO
}
int doit2() {
A i;
int n;
int m;
A* b_ptr = i.transmogrify(n);
// b_ptr->transmogrify(m); // KO, as shown in doit1
static_cast<B*>(b_ptr)->transmogrify(m); // VERY KO see the ASM, but we realy do have a B in the memory pointed by b_ptr
return m + n; // 3 expected, OK == 3, else KO
}
int main() {
return doit1();
// return doit2();
}
Code available at: https://godbolt.org/z/43ebKf1q6
The UB comes from accessing A i; to call the destructor at the end of scope without laundering the pointer. That can let the compiler assume i has not been destroyed by the storage reuse before then.
You need something more like:
alignas(B) std::byte storage[sizeof(B)];
A& i = *new (storage) A;
// ...
static_cast<B*>(std::launder(&i))->~B();
// or: b_ptr->~B();
// or: simply don't call the destructor
I would basically write the following piece of code. I understand why it can't compile.
A instance; // A is a non-default-constructable type and therefore can't be allocated like this
if (something)
{
instance = A("foo"); // use a constructor X
}
else
{
instance = A(42); // use *another* constructor Y
}
instance.do_something();
Is there a way to achieve this behaviour without involving heap-allocation?
There are better, cleaner ways to solve the problem than explicitly reserving space on the stack, such as using a conditional expression.
However if the type is not move constructible, or you have more complicated conditions that mean you really do need to reserve space on the stack to construct something later in two different places, you can use the solution below.
The standard library provides the aligned_storage trait, such that aligned_storage<T>::type is a POD type of the right size and alignment for storing a T, so you can use that to reserve the space, then use placement-new to construct an object into that buffer:
std::aligned_storage<A>::type buf;
A* ptr;
if (cond)
{
// ...
ptr = ::new (&buf) A("foo");
}
else
{
// ...
ptr = ::new (&buf) A(42);
}
A& instance = *ptr;
Just remember to destroy it manually too, which you could do with a unique_ptr and custom deleter:
struct destroy_A {
void operator()(A* a) const { a->~A(); }
};
std::unique_ptr<A, destroy_A> cleanup(ptr);
Or using a lambda, although this wastes an extra pointer on the stack ;-)
std::unique_ptr<A, void(*)(A*)> cleanup(ptr, [](A* a){ a->~A();});
Or even just a dedicated local type instead of using unique_ptr
struct Cleanup {
A* a;
~Cleanup() { a->~A(); }
} cleanup = { ptr };
Assuming you want to do this more than once, you can use a helper function:
A do_stuff(bool flg)
{
return flg ? A("foo") : A(42);
}
Then
A instance = do_stuff(something);
Otherwise you can initialize using a conditional operator expression*:
A instance = something ? A("foo") : A(42);
* This is an example of how the conditional operator is not "just like an if-else".
In some simple cases you may be able to get away with this standard C++ syntax:
A instance=something ? A("foo"):A(42);
You did not specify which compiler you're using, but in more complicated situations, this is doable using the gcc compiler-specific extension:
A instance=({
something ? A("foo"):A(42);
});
This is a job for placement new, though there are almost certainly simpler solutions you could employ if you revisit your requirements.
#include <iostream>
struct A
{
A(const std::string& str) : str(str), num(-1) {};
A(const int num) : str(""), num(num) {};
void do_something()
{
std::cout << str << ' ' << num << '\n';
}
const std::string str;
const int num;
};
const bool something = true; // change to false to see alternative behaviour
int main()
{
char storage[sizeof(A)];
A* instance = 0;
if (something)
instance = new (storage) A("foo");
else
instance = new (storage) A(42);
instance->do_something();
instance->~A();
}
(live demo)
This way you can construct the A whenever you like, but the storage is still on the stack.
However, you have to destroy the object yourself (as above), which is nasty.
Disclaimer: My weak placement-new example is naive and not particularly portable. GCC's own Jonathan Wakely posted a much better example of the same idea.
std::experimental::optional<Foo> foo;
if (condition){
foo.emplace(arg1,arg2);
}else{
foo.emplace(zzz);
}
then use *foo for access. boost::optional if you do not have the C++1z TS implementation, or write your own optional.
Internally, it will use something like std aligned storage and a bool to guard "have I been created"; or maybe a union. It may be possible for the compiler to prove the bool is not needed, but I doubt it.
An implementation can be downloaded from github or you can use boost.
[Global Scope]
myClass *objA, *objB, *obj;
int objnum;
I want to switch between objA and objB and assign them alternatively to obj, so in main() I have:
int main()
{
objA = new myClass(parameters...);
objB = new myClass(parameters...);
// start with objA;
objnum = 0;
obj = objA;
}
At some point a function is called that switches between the two objects:
void switchObjects()
{
if (++objnum > 1) objnum = 0;
obj = objnum == 0 ? objA : objB;
}
And in the function where I use the object, I have:
void doYourJob()
{
int res = obj->work();
}
Now the weird thing is that if I don't assign obj to either objA or objB, it still works. I would expect an exception, instead. Even if I do obj = NULL;, it still works! What's this voodoo?
OK, I could provide a different example that brings to the same result, without using a NULL pointer:
myClass *obj[2];
int objnum;
void switchObject()
{
if (++objnum > 1) objnum = 0;
}
void doYourJob()
{
res = obj[objnum]->work();
}
int main()
{
obj[0] = new myClass(parameters...);
obj[1] = new myClass(parameters...);
objnum = 0;
}
With the above code, regardless of the value of objnum, I still get both objects working together, even if I'm calling work() on only one instance.
And if I replace the function doYourJob() with this:
void doYourJob()
{
int res1 = obj[0]->work();
int res2 = obj[1]->work();
}
I always get the results doubled, as if I were calling the function work() twice on every object.
Consider a simpler example:
#include <iostream>
struct X
{
void foo() { std::cout << "Works" << std::endl; }
};
int main() {
X* x = nullptr;
x->foo();
}
With most compilers and on most platforms, this code will appear to work fine, despite having called foo on a null pointer. However, the behaviour is technically undefined. That is, the C++ language gives no restrictions about what might happen if you do this.
Why does it work? Well, calling a member function only requires knowing the type of the object it is being called on. We know that x points at an X, so we know what function to call: X::foo. In many cases, it may be difficult or even impossible to know if a pointer points at a real object, so the compiler just lets it happen. The body of the function, in this case, doesn't actually depend on the X object actually existing, so it just works. This isn't something you can depend on though.
How would I go about allocating an array of a class without constructing the class, so I could fill up the array later?
I was originally trying to use
Myclass * array = new Myclass[N];
But it tries to construct Myclass to N.
First just declare it without allocating
Myclass * array[N];
when you need it
for(int i=0;i<N;i++){
array[i] = new Myclass(/*params*/);
}
But consider using std::vector/std::list if you must not have to manage memory yourself.
If you really want to do that, (not sure why), you could try
#include <iostream>
using namespace std;
class MyClass
{
public:
MyClass()
{ cout << "helo" << endl; }
};
int main(int argc, char *argv[])
{
int size = 4;
// Here is the trick, pointer to pointer.
MyClass **vec = new MyClass *[size];
cout << "before" << endl;
for (int i = 0; i < 4; ++i)
vec[i] = new MyClass;
// remember to free the vec
return 0;
}
Someone suggested placement new, so here it goes:
// allocate space
std::vector<unsigned char> mybuffer(N * sizeof(Myclass));
Myclass *array = reinterpret_cast<Myclass *>(&mybuffer[0]);
// when you're ready to use it
new( &array[0] ) Myclass(2);
new( &array[1] ) Myclass(3);
// etc...
// when you're done with it
array[0].~Myclass();
array[1].~Myclass();
// etc....
Of course, it is undefined behaviour to use array[x] before you have new'd it, or after you called the destructor.
This is generally something you wouldn't use as a solution to a "normal" problem. Consider actually defining a default constructor that does nothing, and having a function you call later which enhances the objects above their default state.
If you can use C++11, the optimal solution for you is probably std::vector<MyClass> with emplace-base insertions:
class MyClass {
public:
MyClass(int a, bool b, char c); // some non-default constructor
MyClass(double d); // another constructor
void bar();
};
void foo(int n) {
std::vector<MyClass> mv;
mv.reserve(n); // not even needed but beneficial if you know the final size.
// emplace_back uses perfect forwarding to call any arbitrary constructor:
mv.emplace_back(2, false, 'a');
mv.emplace_back(3, true, 'b');
mv.emplace_back(3.1415926535);
// can iterate vector easily:
for (auto &i : mv) {
i.bar();
}
// everything destructed automatically when the collection falls of scope ...
}
This creates the values in the collection directly without a copy and defers any construction of elements until you are ready, unlike new[], which makes a bunch of default objects at array-creation time. It is generally better than placement new as well, since it doesn't leave open opportunities for missed destruction or destructing an invalid memory location as well as being just easier to read.
Alternatively, you may use boost::optional.
So in your case:
std::vector<boost::optional<Myclass>> array(N);
I have a class that resembles this:
However, after the initial constructor, the copy constructor is being called 10 times.
If I don't do the thread creation step. It gets called 4 times which is what I'd expect.
Why is that, and how do I avoid it?
Should I avoid using std::vector in this case and just do new delete instead?
#include <cstdio>
#include <vector>
class A
{
public:
A() { printf("hello\n"); }
~A() { printf("Goodbye\n"); }
A(const A&)
{
printf("copy constructing\n");
}
Thread() { }
};
int main()
{
std::vector<A> a(4, A);
for (int i = 0; i < a.size(); i++){
threads_.create_thread(boost::bind(&A::Thread, a[i]));
}
}
Ok, I found the problem.
This:
threads_.create_thread(boost::bind(&A::Thread, a[i]));
Should be:
threads_.create_thread(boost::bind(&A::Thread, &a[i]));
Take a look at Boost.Ref
for (int i = 0; i < a.size(); i++){
threads_.create_thread(boost::bind(&A::Thread, boost::ref(a[i]) ));
}
this is from the Boost.Bind :
The arguments that bind takes are
copied and held internally by the
returned function object. For example,
in the following code:
int i = 5;
bind(f, i, _1);
a copy of the value of i is stored
into the function object. boost::ref
and boost::cref can be used to make
the function object store a reference
to an object, rather than a copy:
int i = 5;
bind(f, ref(i), _1);
bind(f, cref(42), _1);
Threads are irrelevant here. The "problem" is with boost::bind; it makes copies of the arguments you give it, so it can use the value later when invoked. Use ref so have it store a reference instead.
Should I avoid using std::vector in this case and just do new delete instead?
Huh? Why would you do that? You need to get over the fact copies are made; that just happens. You should be worried about writing clean, maintainable code first, not whether or not you happen to make a copy. There's no reason to use new[] over a std::vector; never use delete.