С этим образцом:
// 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
Но, если я инициализирую v значением r:
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
Я не понимаю две вещи:
Нотабене необработанные строки означают что-то другое в C ++ 11, я думаю, что вы имеете в виду строковый литерал, если вы говорите о "hello"
- Почему с необработанными строками копии mystring не выполняются?
А? Там есть одна копия, поэтому она печатает Const Reference
,
vector<mystring> v{"hello"};
Braced-init-list инициализирует 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);
когда expr
является a
(который является неконстантным lvalue) создание tmp
печать Reference
затем создание tmparray
печать Move
затем копирование tmparray[0]
в векторной печати Const Reference
,
когда expr
является "hello"
создавая элементы tmp
ничего не печатает, а создает tmparray
звонки mystring(const char*)
который ничего не печатает, затем копирует tmparray[0]
в векторной печати Const Reference
,
когда expr
является mystring()
создание tmp
печать Move
затем создание tmparray
печать Move
затем копирование tmparray[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
напечатано один раз tmp2
а также Move
напечатано один раз tmparray
а также Const Reference
напечатаны дважды элементами v
,
Конечно, обычно все это удаляется, поэтому нет ненужных копий или ходов. -fno-elide-constructors
не очень полезно, кроме как выяснить, что произойдет, но на самом деле не происходит!
Вы неправильно пометили конструктор перемещения как noexcept
,