C++ 11强制无条件地移动吗?

Mas*_*Liu 7 c++ gcc c++11

我使用命令编译以下代码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());
}
Run Code Online (Sandbox Code Playgroud)

这是gdb日志:

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
Run Code Online (Sandbox Code Playgroud)

从日志中,我的印象如下:

  1. GCC忽略了复制构造函数s(const s &other).
  2. GCC自动move为其创建一个构造函数class s,然后std::vector.push_back()调用该move构造函数.

    这篇文章指出

    因此,如果类C的定义没有显式声明一个移动赋值运算符,只有满足以下所有条件时,才会隐式声明一个默认值:

    在这里,我的问题是,class s显然有一个用户声明的析构函数~s(),意思class s不符合规定的条件,因此GCC不应该强制执行movestd::vector.push_back().GCC如何表现如此?

  3. 析构~s()函数被调用两次:首先将临时s()函数作为参数传递给ss.push_back(s()); 并移动,然后在std::vector<s>调用析构函数后第二次调用.

  4. 因为这段代码不符合规则,所以在std::vector<s>调用析构函数时注定要崩溃.p对象指向的内容s已被第一个删除~s().因此,std::vector<s>析构函数调用~s()必须崩溃,如同错误double free or corruption.

然而,令我惊讶的是,这个程序以某种方式运行并正常终止.

问题1:为什么程序不崩溃?我只是幸运吗?

问题2:根据gdb日志,是否意味着为先前的C++ 11会议rule of three而不是满足5的规则而设计的代码(如此示例)在编译为C++ 11可执行文件时很可能会崩溃?

编辑:

由于我对gdb这些消息的误解,提出了这个问题:

std::vector<s, std::allocator<s> >::push_back(s&&)
emplace_back(std::move(__x));
std::vector<s, std::allocator<s> >::emplace_back<s>(s&&)
Run Code Online (Sandbox Code Playgroud)

这让我认为GCC创建了一个移动构造函数.我只是做了很多这些专家告诉我的 - 设置一个断点s(const s &other),并注意到程序确实停在那里.这一发现使我的所有问题无效.

正如这里的每位专家所建议的那样,事实是:1.GCC不会创建任何移动构造函数.2.调用现有的复制构造函数.

谢谢大家的帮助!

eer*_*ika 8

GCC忽略了复制构造函数(const s&other).

它可能会这样做,作为复制省略优化.

GCC自动为类s创建移动构造函数,然后调用移动构造函数的std :: vector.push_back()调用.

不会.不会生成移动构造函数.

我的问题是类s显然有一个用户声明的析构函数~s(),这意味着类s不符合第四个条件

正确.该类也不符合第一个条件(用户声明的复制构造函数).

GCC如何表现如此?

海湾合作委员会似乎表现得如此.不涉及移动建设或任务.

析构函数~s()被调用两次

在您显示的gdb日志中,我只看到一个s::~s被调用的实例.

问题1:为什么程序不崩溃?我只是幸运吗?

我觉得你不走运.似乎push_back没有使用复制赋值运算符,因此没有发生双重释放的条件.

问题2:根据gdb日志,它是否意味着为先前的C++ 11设计的代码符合三条规则但不符合五条规则,如此示例...

此示例不符合三个规则,因此这似乎不是您要求的示例.

很可能在编译成C++ 11可执行文件时崩溃?

不可以.只要代码遵循三条规则,就可以安全地复制对象.遵循五的规则只是为了使对象可移动(避免复制).


Luk*_*asz 3

嗯,检查一下:

#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());
}
Run Code Online (Sandbox Code Playgroud)

对我来说,我正在破坏2个不同的对象:
0x7fffc879aa50 默认构造函数
0x1668c40 复制构造函数
0x7fffc879aa50 析构函数
0x1668c40 析构函数

问题1:为什么程序不崩溃?我只是幸运吗?

没有双重的免于同化。

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