Std:: перемещение в списке инициализаторов конструктора в шаблоне класса
У меня есть такой шаблон:
template<typename T>
struct foo {
T m_t;
foo(T t) : m_t(t) {}
};
Проблема в том, что я хочу поддерживать как мелкие/обычные типы, так и огромные типы (например, матрицы) для T
. Рекомендуете ли вы написать список инициализаторов конструктора, как этот
foo (T t) : m_t(std::move(t)) {}
и требуют, чтобы тип T
всегда поддерживал конструкцию перемещения даже для меньших типов? Есть ли лучшие способы?
Ответы
Ответ 1
и требуют, чтобы тип T всегда поддерживал конструкцию перемещения даже для меньших типов?
Любой тип, который является конструируемым, также перемещается конструктивно. Перемещение в этих случаях просто вызывает конструктор копирования. Таким образом, нет оснований не использовать m_t(std::move(t))
.
Альтернативой является использование ссылок вместо:
foo (T const& t) : m_t(t) {}
foo (T&& t) : m_t(std::move(t)) {}
Это может быть связано только с одной конструкцией, а не с двумя.
Ответ 2
Да, использование этого хода не имеет недостатка в этой ситуации. Все объекты, подлежащие копированию, автоматически перемещаются, поэтому это не имеет значения. На самом деле, некоторые рекомендуют всегда перемещать переменные, когда это возможно, даже целые числа.
В качестве альтернативы вы можете использовать совершенную переадресацию, как описано в этом ответе:
template <typename T2>
foo(T2&& t) : m_t(std::forward<T2>(t)) {}
Если вы знаете, что T
определяет конструктор быстрого перемещения, это не имеет значения. В противном случае рекомендуется предоставить конструктор foo(const T&)
, чтобы избежать ненужных копий.
Идеальная пересылка - это только один способ достижения этого. Разумеется, решение Pubby для выписывания конструктора foo(const T&)
и foo(T&&)
также отлично. Результаты те же, это в основном вопрос стиля.
Вы также задали вопрос о небольших целых типах. Теоретически передача их по ссылке медленнее, чем копирование их, но компилятор должен иметь возможность оптимизировать его для копии. Я не думаю, что это будет иметь значение.
Итак, лучше оптимизируйте для худшего случая, когда T
может быть огромным и не обеспечивает быстрый конструктор перемещения. Передача по ссылке лучше для этой ситуации и не должна быть плохим выбором в целом.
Ответ 3
Передача по значению имеет то преимущество, что имеет только один конструктор (без шаблона), но стоит цена одной дополнительной конструкции перемещения по сравнению с вышеупомянутыми альтернативами.
Тем не менее, существует еще один, но не ограниченный недостаток как пропущенного значения, так и решения без шаблонов, заданного Pubby: Moving не будет работать, если конструктор копирования определяется как T(T&);
(обратите внимание на ссылку на не- Const). Rvalue-ссылки могут связываться с lvalue-reference-to-const, но не с lvalue-reference-to-non-const, поэтому компилятор не может вызвать конструктор копирования.
Чтобы решить эту проблему, вы можете просто добавить третью перегрузку foo (T & t) : m_t(t) {}
в решение Pubby.