Должны ли все/большинство функций setter в С++ 11 записываться как шаблоны функций, принимающие универсальные ссылки?

Рассмотрим класс 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, при написании кода в этих сеттерных функциях существуют ли какие-либо существенные недостатки в гипотетическом принципе "записывать функции сеттера в качестве функциональных шаблонов, принимающих универсальные ссылки, когда это возможно"?

Ответ 1

Вы знаете классы A и B, поэтому вы знаете, являются ли они подвижными или нет, и если этот дизайн в конечном итоге необходим. Для чего-то вроде std::string, это пустая трата времени на изменение существующего кода, если вы не знаете, что у вас есть проблема с производительностью. Если вы имеете дело с auto_ptr, тогда вам нужно вырвать его и использовать unique_ptr.

Обычно предпочтительнее принимать аргументы по значению, если вы не знаете ничего более конкретного, например

void set_a(A a) { _a = std::move(a); }

Это позволяет использовать любой из конструкторов A, не требуя ничего, кроме возможности перемещения, и предлагает относительно интуитивно понятный интерфейс.