Можно ли использовать is_tribuly_copy_assignable и is_tribuly_copy_constructible для целей оптимизации?

Кажется, что значения небезопасно копировать с помощью memcpy, если только тип не является тривиально копируемым, т. е. не удовлетворяет признаку типа std::is_trivially_copyable. Интересно, какова цель признаков типа std::is_trivially_copy_assignable, std::is_trivially_copy_constructible, std::is_trivially_move_assignable и std::is_trivially_move_constructible, если вы не можете использовать их для инициализации или назначения с помощью memcpy. Разрешают ли они другие оптимизации?

Мне также интересно, почему стандарт требует тривиального деструктора для копирования значений с помощью memcpy. (Тривиальный деструктор действительно упрощает физическое перемещение значения в памяти, но не кажется принципиально необходимым для простого дублирования значения с помощью memcpy).


person Stephan    schedule 13.10.2012    source источник
comment
Представьте, что деструктор выполняет какую-то работу (что угодно). Если вы просто скопируете (побитово), то вы знаете, что есть два объекта, которые будут выполнять эту работу при запуске их деструктора. Очевидно, тип должен иметь нетривиальный конструктор-копию для начала, но эту проверку деструктора можно рассматривать как крайнюю меру :)   -  person Matthieu M.    schedule 14.10.2012
comment
Если вы действительно хотите продублировать значение, вы ожидаете, что деструктор в конечном итоге запустится дважды. Кроме того, если вы вручную управляете распределением и освобождением, например. в классе-контейнере вы можете просто опустить вызов деструктора для исходного значения перед освобождением или повторным использованием памяти.   -  person Stephan    schedule 14.10.2012


Ответы (3)


Интересно, какова цель признаков типа std::is_trivially_copy_assignable, std::is_trivially_copy_constructible, std::is_trivially_move_assignable и std::is_trivially_move_constructible, если вы не можете использовать их для инициализации или назначения с помощью memcpy

Они сообщают вам свойства типа, разве это не достаточная причина для существования?

У вас может быть тип, который имеет тривиальный конструктор копирования, но нетривиальный конструктор перемещения, поэтому он не будет считаться тривиально копируемым типом, но является тривиально копируемым.

При использовании такого типа вы можете использовать SFINAE или другой статический полиморфизм для включения/отключения определенных операций, если только они не гарантированно тривиальны. Например, представьте себе шаблон класса TrivialPair<A,B>, который мог бы объявить свой конструктор перемещения как удаленный, если A и B не являются тривиально конструируемыми для перемещения, и аналогично для других операций. Это означает, что TrivialPair поддерживает только те операции, которые поддерживаются A и B и которые не вызывают никаких нетривиальных функций.

Мне также интересно, почему стандарт требует тривиального деструктора для копирования значений с помощью memcpy

«Тривиально копируемый» можно рассматривать как указание на то, что тип представляет собой просто набор байтов, то есть просто данные, которые можно безопасно скопировать в другое место в памяти без изменения значения. Если у типа есть нетривиальный деструктор, то это не просто куча байтов, у него есть какое-то дополнительное поведение, которое не понимает компилятор и которое может сделать использование memcpy небезопасным.

person Jonathan Wakely    schedule 13.10.2012
comment
Мне просто любопытно, есть ли смысл знать, тривиальны ли отдельные методы (конструктор копирования, оператор присваивания и т. д.). Когда тип тривиально копируем (т. е. удовлетворяет is_tribuly_copy) или когда гарантированно не будет генерироваться исключение, вы можете использовать это для оптимизации, но зачем вам определять тип, подобный вашему типу TrivialPair? - person Stephan; 14.10.2012

Конечно. Можно написать шаблон векторного класса, который использует это и вызывает std::memcpy в своем конструкторе копирования, если это безопасно. Я полагаю, что по крайней мере одна реализация std::copy использует аналогичную оптимизацию для случая, когда итераторы имеют тип T*, а T легко копируется.

person sellibitze    schedule 13.10.2012
comment
Разрешает ли стандарт копирование с помощью memcpy, если класс, например. удовлетворяет std::is_trivially_copy_constructible и std::is_trivially_copy_assignable, но не std::is_trivially_copyable? Или что означает ваш Sure. Ссылаться на? - person Stephan; 14.10.2012

Я предполагаю, что это точно так же, как и любой другой придирки в стандарте. Где-то там Джо и Джейн будут говорить о том, что char имеет 8 бит, а Боб вопит о том, что стандарт говорит, что это на самом деле «8 или БОЛЕЕ бит!» На следующий день они будут заниматься каламбуром, копируя число с плавающей запятой в uint32_t и немного подкручивая его на основе IEEE-754, и Боб снова вопит о том, что IEEE-754 не является строго обязательным, и он будет настаивать на том, что небольшая возня, который они делают, может привести к тому, что какая-то теоретическая машина взорвет вселенную.

Черт возьми, Боб!

На практике совершенно очевидно, что std::is_tribuly_copy_assignable означает, что выражение «A = B» будет просто оцениваться как memcpy(), так что дерзайте и пользуйтесь преимуществом. Конечно, может быть какая-то непонятная реализация, которая изо всех сил старается показать вам палец, когда вы используете memcpy() тип, который не является строго std::is_tribuly_copyable, и я уверен, что Боб, вероятно, является автором указанной реализации. , но вам действительно все равно?

Это всего лишь мое предположение, заметьте. У меня есть некоторый умеренный опыт в этой области, но я ни в коем случае не являюсь экспертом мирового класса по мелочам C++. Если я ошибаюсь в этом (на практике, а не только в теории), пожалуйста, дайте мне знать. Я также хотел бы увидеть машину с CHAR_BIT> 8 (о чем я бы действительно заботился) или машину с плавающей запятой, отличной от IEEE-754 (опять же, о которой я бы действительно заботился).

person fieldtensor    schedule 17.07.2017