Должны ли все / большинство установочных функций в C++11 быть написаны как шаблоны функций, принимающие универсальные ссылки?
Рассмотрим класс X
с N
переменные-члены, каждая из которых имеет некоторый копируемый и подвижный тип, и N
соответствующие функции сеттера. В C++98 определение X
скорее всего будет выглядеть примерно так:
class X
{
public:
void set_a(A const& a) { _a = a; }
void set_b(B const& b) { _b = b; }
...
private:
A _a;
B _b;
...
};
Сеттер-функции класса X
Выше можно связать как с lvalue, так и с rvalue аргументами. В зависимости от фактического аргумента, это может привести к созданию временного и в конечном итоге приведет к назначению копии; из-за этого не копируемые типы не поддерживаются этим дизайном.
В C++11 у нас есть семантика перемещения, идеальная пересылка и универсальные ссылки (терминология Скотта Мейерса), которые позволяют более эффективно и обобщенно использовать функции сеттера, переписав их следующим образом:
class X
{
public:
template<typename T>
void set_a(T&& a) { _a = std::forward<T>(a); }
template<typename T>
void set_b(T&& b) { _b = std::forward<T>(b); }
...
private:
A _a;
B _b;
...
};
Универсальные ссылки могут связываться с const
/ не- const
, volatile
/ не- volatile
и к любому конвертируемому типу в целом, избегая создания временных и передачи значений прямо operator =
, Не копируемые, подвижные типы теперь поддерживаются. Возможно, нежелательные привязки могут быть устранены либо через static_assert
или через std::enable_if
,
Поэтому мой вопрос таков: должны ли все (скажем, большинство) сеттер-функции в C++11 быть написаны как шаблоны функций, принимающие универсальные ссылки?
Помимо более громоздкого синтаксиса и невозможности использования вспомогательных инструментов, подобных Intellisense, при написании кода в этих функциях установки, есть ли какие-либо существенные недостатки с гипотетическим принципом " записывать функции установки как шаблоны функций, принимающие универсальные ссылки, когда это возможно "?
1 ответ
Вы знаете классы A и B, так что вы знаете, являются ли они подвижными или нет, и нужна ли эта конструкция в конечном итоге. Для чего-то вроде std::string
изменение существующего кода - пустая трата времени, если только вы не знаете, что у вас здесь проблемы с производительностью. Если вы имеете дело с auto_ptr
тогда пришло время вырвать его и использовать unique_ptr
,
Обычно предпочтительнее принимать аргументы по значению, если вы не знаете ничего более конкретного, такого как
void set_a(A a) { _a = std::move(a); }
Это позволяет использовать любой из конструкторов A
не требуя ничего кроме подвижности и предлагает относительно интуитивно понятный интерфейс.