Masao Liu Masao Liu - 1 day ago 4
C++ Question

Does C++11 force move unconditionally?

I compile the following code using 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 log, I am in the following impression:


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

  2. GCC automatically creates a
    move
    constructor for
    class s
    and then
    std::vector.push_back()
    calls that
    move
    constructor.

    This article states

    Thus, if the definition of class C doesn't explicitly
    declare a move assignment operator, one will be implicitly
    declared as defaulted only if all of the following conditions
    are met:

    My question here is that
    class s
    apparently has a user-declared destructor
    ~s()
    , meaning
    class s
    does not meet the forth condition and thus GCC should not enforce
    move
    on
    std::vector.push_back()
    . How does GCC behaves so?

  3. Destructor
    ~s()
    is called two times: first immediately after temporary
    s()
    has been passed as argument to
    ss.push_back(s());
    and moved, and second after the destructor of
    std::vector<s>
    is called.

  4. Because this code does not meet rule of three, it is doomed to crash when the destructor of
    std::vector<s>
    is called. The content pointed to by
    p
    in object
    s
    is already deleted by the first
    ~s()
    . Therefore,
    std::vector<s>
    destructor calling
    ~s()
    must crash with error like
    double free or corruption
    .



However, much to my surprise, somehow this program runs and terminates normally.

question 1: Why the program does not crash? Am I simply lucky?

question 2: According to
gdb
log, does it mean that codes designed for prior C++11 meeting
rule of three
but not meeting rule of five, like this example, are very likely to crash when they are compiled to C++11 executables?

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 lead me to think that GCC creates a move constructor. I just did as many these experts told me to - setting a break point in
s(const s &other)
, and noticed that the program does stop there. This finding invalidates all my questions.

As every expert here suggested, the facts are:
1. GCC does not create any move constructor.
2. The existing copy constructor is called.

Thank you all for the big helps!

Answer

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 am getting destructed 2 different object:
0x7fffc879aa50 default constructor
0x1668c40 copy constructor
0x7fffc879aa50 destructor
0x1668c40 destructor

question 1: Why the program does not crash? Am I simply lucky?

There is no double free of coraption.

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

Comments