У меня есть несколько классов, для которых я хочу проверить, генерируется ли конструктор перемещения по умолчанию. Есть ли способ проверить это (будь то утверждение времени компиляции или синтаксический анализ созданных объектных файлов или что-то еще)?
Мотивационный пример:
class MyStruct : public ComplicatedBaseClass {
std::vector<std::string> foo; // possibly huge
ComplicatedSubObject bar;
};
Если какой-либо член любой базы или члена любого из классов Complicated...Object
не может быть перемещен, MyStruct
не будет иметь сгенерированный им неявный конструктор перемещения и, таким образом, не сможет оптимизировать работу копирования foo
, когда перемещение может быть выполнено, хотя foo
является подвижным.
Я хочу избежать:
- утомительно проверяя условия для неявного перемещения ctor,
- явно и рекурсивно дефолтировать специальные функции-члены всех затронутых классов, их баз и их членов. Просто убедитесь, что доступен конструктор перемещения.
Я уже пробовал следующее, и они не работают:
- используйте
std::move
явно:— это вызовет копию ctor, если нет перемещения ctor. - use
std::is_move_constructible
— это будет успешным, если есть конструктор копирования, принимающийconst Type&
, который генерируется по умолчанию (если конструктор перемещения явно не удален, по крайней мере,). - используйте
nm -C
, чтобы проверить наличие перемещения ctor [см. ниже]. Однако альтернативный подход является жизнеспособным [см. Ответ].
Я попытался просмотреть созданные символы тривиального класса следующим образом:
#include <utility>
struct MyStruct {
MyStruct(int x) : x(x) {}
//MyStruct(const MyStruct& rhs) : x(rhs.x) {}
//MyStruct(MyStruct&& rhs) : x(rhs.x) {}
int x;
};
int main() {
MyStruct s1(4);
MyStruct s2(s1);
MyStruct s3(std::move(s1));
return s1.x+s2.x+s3.x; // make sure nothing is optimized away
}
Сгенерированные символы выглядят следующим образом:
$ CXXFLAGS="-std=gnu++11 -O0" make -B x; ./x; echo $?; nm -C x | grep MyStruct | cut -d' ' -f3,4,5
g++ -std=gnu++11 -O0 x.cc -o x
12
.pdata$_ZN8MyStructC1Ei
.pdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
.text$_ZN8MyStructC1Ei
.text$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
.xdata$_ZN8MyStructC1Ei
.xdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
MyStruct::MyStruct(int)
std::remove_reference<MyStruct&>::type&&
Результат тот же, когда я явно по умолчанию копирует и перемещает ctors (без символов).
С моей собственной копией и перемещением ctors вывод выглядит следующим образом:
$ vim x.cc; CXXFLAGS="-std=gnu++11 -O0" make -B x; ./x; echo $?; nm -C x | grep MyStruct | cut -d' ' -f3,4,5
g++ -std=gnu++11 -O0 x.cc -o x
12
.pdata$_ZN8MyStructC1Ei
.pdata$_ZN8MyStructC1EOKS_
.pdata$_ZN8MyStructC1ERKS_
.pdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
.text$_ZN8MyStructC1Ei
.text$_ZN8MyStructC1EOKS_
.text$_ZN8MyStructC1ERKS_
.text$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
.xdata$_ZN8MyStructC1Ei
.xdata$_ZN8MyStructC1EOKS_
.xdata$_ZN8MyStructC1ERKS_
.xdata$_ZSt4moveIR8MyStructEONSt16remove_referenceIT_E4typeEOS3_
MyStruct::MyStruct(int)
MyStruct::MyStruct(MyStruct&&)
MyStruct::MyStruct(MyStruct const&)
std::remove_reference<MyStruct&>::type&& std::move<MyStruct&>(MyStruct&)
Итак, похоже, этот подход также не работает.
Однако, если у целевого класса есть член с явным конструктором перемещения, неявно созданный конструктор перемещения будет виден для целевого класса. То есть с этим кодом:
#include <utility>
struct Foobar {
Foobar() = default;
Foobar(const Foobar&) = default;
Foobar(Foobar&&) {}
};
struct MyStruct {
MyStruct(int x) : x(x) {}
int x;
Foobar f;
};
int main() {
MyStruct s1(4);
MyStruct s2(s1);
MyStruct s3(std::move(s1));
return s1.x+s2.x+s3.x; // make sure nothing is optimized away
}
Я получу символ для MyStruct
move ctor, но не копию ctor, поскольку он кажется полностью неявным. Я предполагаю, что компилятор генерирует тривиальное вложенное перемещение ctor, если это возможно, и нетривиальное, если он должен вызывать другие нетривиальные перемещения. Это все равно не помогает мне в моих поисках.