Рассмотрим класс X
с N
переменными-членами, каждый из которых является скопированным и подвижным, и N
соответствующими функциями сеттера.
В С++ 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. В зависимости от фактического аргумента это может привести к созданию временного и в конечном итоге приведет к назначению копии; из-за этого, не подлежащие копированию типы не поддерживаются этой конструкцией.
С С++ 11 мы имеем семантику перемещения, совершенную пересылку и универсальные ссылки (терминология Скотта Мейерса), которые позволяют более эффективно и обобщенно использовать функции setter, переписывая их следующим образом:
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
/non const
, volatile
/non volatile
и к любому конвертируемому типу вообще, избегая создания временных и передающих значений прямо к operator =
. Теперь поддерживаются несжимаемые, подвижные типы. Возможно, нежелательные привязки можно устранить либо через static_assert
, либо через std::enable_if
.
Итак, мой вопрос: как руководство по дизайну, должны ли все (пусть и большинство) функции setter в С++ 11 записываться как шаблоны функций, принимающие универсальные ссылки?
Помимо более громоздкого синтаксиса и невозможности использования вспомогательных инструментов, подобных Intellisense, при написании кода в этих сеттерных функциях существуют ли какие-либо существенные недостатки в гипотетическом принципе "записывать функции сеттера в качестве функциональных шаблонов, принимающих универсальные ссылки, когда это возможно"?