Правила добавления неявно сгенерированных (= сгенерированных компилятором) конструкторов перемещения довольно консервативны: добавляйте только один, если это безопасно. Если конструктор копирования был определен пользователем, компилятор не может предположить, что добавление какого-либо простого конструктора перемещения, сгенерированного компилятором, по-прежнему безопасно, поэтому конструктор перемещения добавлен не будет. На самом деле достаточно объявить его, чтобы предотвратить создание конструктора перемещения, IIRC, чтобы разрешить типы только для копирования.
Шаблоны конструкторов никогда не рассматриваются ни как конструкторы копирования, ни как конструкторы перемещения, поэтому они не препятствуют их неявному созданию.
Конечно, если конструктор перемещения никогда не объявлялся, вместо него можно выбрать другой конструктор с помощью разрешения перегрузки. Рассмотрим этот пример:
#include <iostream>
#include <utility>
struct loud
{
loud() { std::cout << "default ctor\n"; }
loud(loud const&) { std::cout << "copy ctor\n"; }
loud(loud&&) { std::cout << "move ctor\n"; }
};
struct foo
{
loud l;
};
struct bar
{
loud l;
bar() = default;
bar(bar const&) = default;
};
int main()
{
foo f0;
foo f1(f0);
foo f2(std::move(f0));
std::cout << "--------------\n";
bar b0;
bar b1(b0);
bar b2(std::move(b0));
}
Выход:
default ctor
copy ctor
move ctor
--------------
default ctor
copy ctor
copy ctor
Здесь foo
получит неявно объявленный конструктор по умолчанию, копирование и перемещение, тогда как bar
не получит неявно объявленный конструктор перемещения, поскольку он имеет объявленный пользователем конструктор копирования.
Во втором случае нет неявно объявленного конструктора перемещения, поэтому разрешение перегрузки для Test<int> testInt4(std::move(testInt2))
предпочитает шаблон конструктора конструктору копирования, поскольку первый использует ссылку с меньшей квалификацией cv.
Чтобы уменьшить дублирование кода, вы можете делегировать создание:
template <class T>
class Test {
T _temp;
struct tag {};
public:
Test() {
std::cout << "Test()" << std::endl;
};
Test(Test const& test)
: Test(test, tag{})
{}
Test(Test&& test)
: Test(std::move(test), tag{})
{}
template <class T2> Test(Test<T2> const &test, tag = {}) {
std::cout << "template <class T2> Test(Test<T2> const &test)" << std::endl;
};
template <class T2> Test(Test<T2> &&test, tag = {}) {
std::cout << "template <class T2> Test(Test<T2> &&test)" << std::endl;
};
};
Кстати, вы также можете уменьшить некоторые шаблоны, используя шаблон конструктора с идеальной переадресацией вместо двух шаблонов конструктора:
Используя эту черту:
template<class T>
struct is_Test : std::false_type {};
template<class T>
struct is_Test<Test<T>> : std::true_type {};
Вы можете определить:
template <class T2,
class = typename std::enable_if<is_Test<T2>::value>::type>
Test(T2&& test, tag = {}) {
std::cout << "template <class T2> Test(T2&& test)" << std::endl;
};
person
dyp
schedule
30.03.2014