Is the power of C ++ 11 moving unconditionally?

I will compile the following code with the command g++ -std=c++11 t.cpp :

 #include <vector> #include <cstring> //memcpy() class s { char *p; size_t size; public: s(){ size=10; p=new char[size]; } s(const s &other){ size=other.size; p=new char[size]; memcpy(p,other.p,other.size); } ~s(){ delete [] p; } }; int main() { std::vector<s> ss; ss.push_back(s()); } 

This is gdb log:

 Breakpoint 1, main () at t.cpp:23 23 ss.push_back(s()); s::s (this=0x7fffffffe370) at t.cpp:9 9 size=10; 10 p=new char[size]; 11 } std::vector<s, std::allocator<s> >::push_back(s&&) (this=0x7fffffffe350, __x=<unknown type in /tmp/a.out, CU 0x0, DIE 0x20d0>) at /usr/include/c++/4.9/bits/stl_vector.h:932 932 { emplace_back(std::move(__x)); } std::move<s&> (__t=...) at /usr/include/c++/4.9/bits/move.h:102 102 { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); } std::vector<s, std::allocator<s> >::emplace_back<s>(s&&) (this=0x7fffffffe350) at /usr/include/c++/4.9/bits/vector.tcc:94 94 if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) 101 _M_emplace_back_aux(std::forward<_Args>(__args)...); 102 } s::~s (this=0x7fffffffe370, __in_chrg=<optimized out>) at t.cpp:17 17 ~s(){ delete [] p; } std::vector<s, std::allocator<s> >::~vector (this=0x7fffffffe350, __in_chrg=<optimized out>) at /usr/include/c++/4.9/bits/stl_vector.h:425 

From the magazine I get the following impression:

  • GCC ignores the copy constructor s(const s &other) .
  • GCC automatically creates a move constructor for class s , and then std::vector.push_back() calls the move constructor.

    This article states

    Thus, if the definition of class C does not explicitly declare a move assignment operator, one will be implicitly defaulted only if the following conditions are true:

    My question here is that class s apparently has a user-declared destructor ~s() , which means that class s does not meet the fourth condition, and therefore GCC should not apply move to std::vector.push_back() . How does GCC behave like this?

  • The destructor ~s() is called two times: first immediately after the temporary s() was passed as the argument ss.push_back(s()); and moved , and the second after the destructor, std::vector<s> is called.

  • Since this code does not comply with rule three , it is doomed to fail when the destructor std::vector<s> called. The content pointed to by p in the s object is already deleted first ~s() . Therefore, the call to std::vector<s> ~s() destructor should be in error, like double free or corruption .

However, to my great surprise, somehow this program starts and finishes normally.

Question 1 : Why is the program not crashing? I was just lucky?

question 2 : according to gdb log, does this mean that the codes intended for the previous C ++ 11 meet the rule of three , but do not correspond to the rule of five , as this example, with a high probability it will fail when compiling in C ++ 11 executable files?

EDIT

This question was raised due to my misinterpretation of gdb messages like these:

 std::vector<s, std::allocator<s> >::push_back(s&&) emplace_back(std::move(__x)); std::vector<s, std::allocator<s> >::emplace_back<s>(s&&) 

which make me think that GCC is creating a move constructor. I just did as many experts told me, setting a breakpoint in s(const s &other) and noticed that the program stopped there. This discovery will nullify all my questions.

As each expert suggested, the facts are as follows: 1. GCC does not create a move constructor. 2. The existing copy constructor is called.

Thank you all for your help!

+7
c ++ gcc c ++ 11
source share
3 answers

hmmm check this:

 #include <vector> #include <cstring> //memcpy() #include <iostream> class s { char *p; size_t size; public: s(){ std::cout<<this<<"\tdefault constructor\n"; size=10; p=new char[size]; } s(const s &other){ std::cout<<this<<"\tcopy constructor\n"; size=other.size; p=new char[size]; memcpy(p,other.p,other.size); } ~s(){ std::cout<<this<<"\tdestructor\n"; delete [] p; } }; int main() { std::vector<s> ss; ss.push_back(s()); } 

For me, I destroy 2 different objects:
Default constructor 0x7fffc879aa50
Copy Designer 0x1668c40
0x7fffc879aa50 destructor
0x1668c40 destructor

Question 1: Why is the program not crashing? I was just lucky?

There is no double free from attachment.

compiled with: g ++ -O3 --std = C ++ 11 file2.cpp -o file2.out
g ++ 4.7.3

+3
source share

GCC ignores the copy constructor s (const s & other).

It is possible that this will be an optimization when copying.

GCC automatically creates a move constructor for class s, and then calls std :: vector.push_back () to move the constructor.

Not. The move constructor is not generated.

My question here is that class s apparently has a user-declared destructor ~ s (), which means that class s does not meet the fourth condition

Correctly. The class also does not meet the first condition (the declared copy constructor).

How does GCC behave like this?

GCC seems to be behaving as it should. The design or purpose of the movement is not related.

The destructor ~ s () is called twice

In the gdb log you are showing, I see only one instance of s::~s that is being called.

Question 1: Why is the program not crashing? I was just lucky?

I think you are out of luck. It seems that push_back did not use the copy assignment operator, so there were no conditions for double release.

question 2: According to gdb log, does this mean that the codes intended for the previous C ++ 11 comply with rule three, but do not comply with rule five, as this example ...

This example does not comply with rule three, so this is not an example of what you are asking for.

may fail when compiling executable files in c ++ 11?

Not. As long as the code follows the rule of three, you can copy objects. Following rule five, it is only necessary to make the object movable (to avoid copying).

+8
source share

If the copy constructor is explicitly declared (as in your case), the constructor or assignment constructor is automatically generated by the compiler.

The copy constructor is the best candidate and is used to copy the temporarily generated element (both the default constructor and the copy instance are called).

It also gives a strong guarantee of the safety of exceptions .

+3
source share

All Articles