¿Todas / la mayoría de las funciones de setter en C++11 deben escribirse como plantillas de función que acepten referencias universales?


Considere una clase X con variables miembro N, cada uno de algunos tipos copiablesy móviles, y N funciones de setter correspondientes. En C++98, la definición de X probablemente se vería algo como esto:

class X
{
public:
    void set_a(A const& a) { _a = a; }
    void set_b(B const& b) { _b = b; }
    ...
private:
    A _a;
    B _b;
    ...
};

Las funciones Setter de la clase X anterior pueden enlazar tanto a los argumentos lvalue como a rvalue. Dependiendo del argumento real, este podría resultar en la creación de un temporal y eventualmente resultará en una copia asignación; debido a esto, los tipos no copiables no son compatibles con este diseño.

Con C++11 tenemos semántica de movimiento, reenvío perfecto y referencias universales (terminología de Scott Meyers), que permiten un uso más eficiente y generalizado de las funciones setter reescribiéndolas de esta manera:

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;
    ...
};

Las referencias universales pueden enlazar a const/non-const, volatile/no-volatile, y a cualquier tipo convertible en general, evitando la creación de temporarios y valores pasantes directamente a operator =. No copiable, ahora se admiten los tipos movible. Las posibles vinculaciones no deseadas pueden eliminarse a través de static_assert o a través de std::enable_if.

Así que mi pregunta es: como una guía de diseño , ¿deberían todas (digamos, la mayoría) las funciones setter en C++11 ser escritas como plantillas de función que acepten referencias universales?

Aparte de la sintaxis más engorrosa y la imposibilidad de usar herramientas de ayuda similares a Intellisense al escribir código en esas funciones setter, ¿hay alguna desventaja relevante con el principio hipotético"escribir funciones setter como plantillas de función que acepten referencias universales siempre que sea posible"?

Author: Andy Prowl, 2013-01-07

1 answers

Conoces las clases A y B, así que sabes si son móviles o no y si este diseño es necesario en última instancia. Para algo como std::string, es una pérdida de tiempo cambiar el código existente a menos que sepa que tiene un problema de rendimiento aquí. Si estás tratando con auto_ptr, entonces es hora de arrancarlo y usar unique_ptr.

Normalmente se prefiere ahora tomar argumentos por valor si no sabe nada más específico, como

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

Esto permite el uso de cualquiera de los constructores de A sin requerir nada excepto movilidad y ofrece una interfaz relativamente intuitiva.

 36
Author: Puppy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-07 14:10:25