0

使用此示例:

// test.cpp

#include <iostream>
#include <vector>
#include <utility>

using namespace std;

class mystring : public string { public:
    mystring() = default;

    mystring(const char* c) : string(c) {}

    mystring(mystring& m) : string(m) { cout << "Reference" << endl; }
    mystring(mystring const & m) : string(m) { cout << "Const reference" << endl; }
    mystring(mystring&& m) : string(move(m)) { cout << "Move" << endl; }
};

int main() {
    mystring a;

    vector<mystring> v{ a };
}

输出是:

$ g++ --version | grep "g++"
g++ (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2
$ g++ -std=c++11 -fno-elide-constructors test.cpp
$ ./a.out
Reference
Move
Const reference

但是,如果我用 r 值初始化 v:

vector<mystring> v{"hello"};

输出是:

$ g++ -std=c++11 -fno-elide-constructors test.cpp
$ ./a.out
Const reference

这意味着,没有副本。使用 r 值:

vector <mystring> v{mystring()};

输出:

$ g++ -std=c++11 -fno-elide-constructors test.cpp
$ ./a.out
Move
Move
Const reference

我不明白两件事:

  • 为什么使用非原始字符串执行第二次移动(在第一次复制/移动之前)?
  • 为什么使用原始字符串,不执行“mystring”的副本?
4

2 回答 2

2

NB原始字符串在C ++ 11中意味着不同的东西,如果你在谈论,我认为你的意思是字符串文字"hello"

  • 为什么使用原始字符串,不执行“mystring”的副本?

诶?有一个副本,这就是它打印的原因Const Reference

vector<mystring> v{"hello"};

大括号初始化列表std::initializer_list<mystring>使用单个元素初始化构造函数参数,该元素使用mystring(const char* c)构造函数构造,然后使用构造函数将该元素复制到向量中mystring(const mystring&),打印Const Reference. 很简单。

  • 为什么使用非原始字符串执行第二次移动(在第一次复制/移动之前)?

第二招怎么可能在第一招之前?!:)

这是一个如何initializer_list构造的实现细节,这通常无关紧要,因为额外的动作会被省略。

vector <mystring> v{mystring()};

mystring这将使用默认构造函数创建一个临时对象,然后std::initializer_list<mystring>使用单个元素创建一个元素,该元素是使用mystring(mystring&&)构造函数以临时对象作为其参数来构造的。那招施工印Move。内部还有另一个动作,再次打印Move。然后initializer_list像以前一样将元素复制到向量中,打印Const Reference

要回答您的评论:

您看到的额外移动也发生在第一种情况下,但在这种情况下,被移动的类型const char*不会打印Move,即该语句std::vector<mystring>{ expr };可以被认为是:

auto tmp = expr;
mystring tmparray[] = { std::move(tmp) };
std::initializer_list<mystring> init_list( tmparray, 1 };
std::vector<mystring> v(init_list);

何时expra(这是一个非常量左值)创建tmpprintsReference然后创建tmparrayprintsMove然后复制tmparray[0]到向量 printsConst Reference中。

何时创建不打印任何expr内容"hello"的元素tmp,并创建不打印任何内容的tmparray调用mystring(const char*),然后复制tmparray[0]到向量 printsConst Reference中。

何时expr创建打印然后创建mystring()打印然后复制到矢量打印tmpMovetmparrayMovetmparray[0]Const Reference

使用像这样的两个元素支撑初始化列表,{ expr1, expr2 }它可以是:

auto tmp1 = expr1;
auto tmp2 = expr2;
mystring tmparray[] = { std::move(tmp1), std::move(tmp2) };
std::initializer_list<mystring> init_list( tmparray, 2 };
std::vector<mystring> v(init_list);

因此,如果您使用,{ "hello", mystring() }您将Move被.tmp2MovetmparrayConst Referencev

当然,通常所有这些都会被忽略掉,因此没有不必要的复制或移动。 -fno-elide-constructors除了找出会发生什么之外并没有真正有用,但实际上并没有发生!

于 2012-12-14T13:32:57.403 回答
0

您没有正确地将移动构造函数标记为noexcept.

于 2012-12-14T13:23:53.363 回答