Ответ 1
Либо это была моя ошибка, либо я был неправильно истолкован, и я честно не помню, какой из них.
Однако у меня есть очень прочные советы по этому вопросу:
Не используйте
is_trivial
иis_trivially_copyable
! EVER!!!
Вместо этого используйте один из них:
is_trivially_destructible<T>
is_trivially_default_constructible<T>
is_trivially_copy_constructible<T>
is_trivially_copy_assignable<T>
is_trivially_move_constructible<T>
is_trivially_move_assignable<T>
Обоснование:
TL;DR: См. этот отличный вопрос и правильный ответ.
Никто (включая меня) не может запомнить определение is_trivial
и is_trivially_copyable
. И если вы действительно посмотрите его, а затем потратите 10 минут на его анализ, он может или не может делать то, что вы интуитивно думаете. И если вам удастся его правильно проанализировать, CWG может изменить свое определение с небольшим или отсутствующим уведомлением и аннулировать ваш код.
Использование is_trivial
и is_trivially_copyable
воспроизводится с огнем.
Однако эти:
is_trivially_destructible<T>
is_trivially_default_constructible<T>
is_trivially_copy_constructible<T>
is_trivially_copy_assignable<T>
is_trivially_move_constructible<T>
is_trivially_move_assignable<T>
делают именно то, что они звучат так, как они, и вряд ли когда-либо изменит свое определение. Может показаться чересчур многословным иметь дело с каждым из специальных членов по отдельности. Но это окупится в стабильности/надежности вашего кода. И если вам нужно, упакуйте эти индивидуальные черты в пользовательский признак.
Обновление
Например, clang и gcc компилируют эту программу:
#include <type_traits>
template <class T>
void
test()
{
using namespace std;
static_assert(!is_trivial<T>{}, "");
static_assert( is_trivially_copyable<T>{}, "");
static_assert( is_trivially_destructible<T>{}, "");
static_assert( is_destructible<T>{}, "");
static_assert(!is_trivially_default_constructible<T>{}, "");
static_assert(!is_trivially_copy_constructible<T>{}, "");
static_assert( is_trivially_copy_assignable<T>{}, "");
static_assert(!is_trivially_move_constructible<T>{}, "");
static_assert( is_trivially_move_assignable<T>{}, "");
}
struct X
{
X(const X&) = delete;
};
int
main()
{
test<X>();
}
Обратите внимание, что X
тривиально скопируема, но не тривиально копируется. Насколько мне известно, это соответствует поведению.
В VS-2015 в настоящее время говорится, что X
ни тривиально не копируется, ни тривиально копируется конструктивно. Я считаю, что это неправильно в соответствии с текущей спецификацией, но это точно соответствует моему здравому смыслу.
Если мне понадобилось memcpy
для неинициализированной памяти, я бы доверял is_trivially_copy_constructible
над is_trivially_copyable
, чтобы убедить меня, что такая операция будет в порядке. Если бы я хотел memcpy
инициализировать память, я бы проверил is_trivially_copy_assignable
.