punteros no opcionales vs. referencias no constantes en C ++

12

En Otras características de C ++, Argumentos de referencia de la Guía de estilo de Google C ++ , leí que no se deben usar referencias no constantes.

Todos los parámetros pasados ​​por referencia deben etiquetarse const.

Está claro que mirar las llamadas a funciones que usan referencias como argumentos es absolutamente confuso para los programadores de C, pero C y C ++ son lenguajes diferentes ahora. Si se requiere un parámetro de salida , el uso de un puntero para un parámetro de salida requerido puede hacer que se omita todo el cuerpo de la función, lo que hace que la implementación de una función sea más complicada (aumenta formalmente la complejidad y profundidad ciclomática de una función).

Me gustaría hacer que el código C ++ sea lo más fácil de entender / mantener posible, por lo que generalmente estoy interesado en leer las guías de estilo de codificación. Pero para adaptar las mejores prácticas en un equipo, creo que comprender el fundamento de los elementos de la guía de estilo es un factor importante.

¿Las referencias no constantes son realmente tan malas? ¿Prohibirlos solo es específico de Google o es una regla comúnmente aceptada? ¿Qué justifica el esfuerzo adicional para implementar parámetros de salida como punteros?

Lobo
fuente
2
"el uso de un puntero hace que se omita todo el cuerpo de la función" er, ¿qué?
monstruo de trinquete
@ratchetfreak Intenté aclarar esto. Admito que funciones como esta pueden mostrar algunos defectos de diseño. Un puntero siempre es formalmente opcional, por lo que debe verificarse antes de desreferenciarlo.
Wolf
44
La guía de estilo C ++ de Google es bastante al revés. En mi opinión subjetiva, debería quemarse.
Siyuan Ren
En cuanto a este elemento en particular, creo que la razón es que obligar a los programadores a escribir un ampersand y cuando los argumentos pueden estar mutados muestran una intención más clara.
Siyuan Ren
44
La Guía de estilo de Google se escribió tal como estaba, para admitir código homogéneo en proyectos heredados de Google. Si no está trabajando en proyectos heredados (que se escribieron con esta guía de estilo desde el principio), probablemente no debería usarla (especifica muchas reglas que no son buenas para el nuevo código (c ++ 11, c ++ 14 , c ++ 17)).
utnapistim

Respuestas:

18

La razón detrás de la guía de estilo de Google es simplemente dejar en claro desde el sitio de llamadas de una función si un parámetro es un parámetro de entrada o un parámetro de salida. (Ver aquí para una discusión más detallada). Otros lenguajes establecen parámetros explícitos por diseño; C #, por ejemplo, tiene una outpalabra clave que debe usarse en el sitio de la llamada . Como C ++ no lo hace explícito, Google eligió usar const ref. versus puntero para que quede claro.

¿Es esto solo una regla de Google? No, pero dudo que esté muy extendido. No creo haberlo visto fuera de la guía de estilo y los grupos de Google que se adhieren explícitamente a partes de la guía de estilo de Google. (Por ejemplo, me gustó la idea cuando leí por primera vez la guía de estilo de Google hace años y la he usado para algunos de mis propios códigos).

En particular, las recientemente anunciadas Pautas principales de C ++ prefieren los valores de retorno a los parámetros de salida para (casi) todo y utilizan referencias sin constantes para el resto. El uso de punteros de Google en comparación con las referencias podría aclarar los parámetros de salida, pero los valores de retorno son aún más claros. Ahora que C ++ 11 tiene movimientos estandarizados (referencias de valor, &&para hacer que los retornos de muchos tipos sean baratos) y tuplas (lo que permite una manera fácil de devolver múltiples valores), muchos de los casos de uso para parámetros ya no se aplican.

Las Pautas principales de C ++ tienen algunos grandes nombres (Bjarne Stroustrup, Herb Sutter) detrás de ellas, son compatibles con Microsoft y adoptan las últimas características de C ++ (a diferencia de la guía de estilo de Google), por lo que espero que sus recomendaciones sean más populares que las de Google.

Josh Kelley
fuente
Gracias por su respuesta (también por la breve excursión a C #). La revisión fácil es, por supuesto, un punto importante, especialmente en proyectos de código abierto. Con retornos baratos en C ++ moderno, estas consideraciones perderán su importancia. Con el software antiguo y sus compiladores antiguos, puede ser útil.
Wolf
No lo olvidé, pero las Pautas principales de C ++ no son tan rápidas de obtener. Es interesante que su filosofía muestre las razones detrás de las reglas y también una visión modernizada de la programación ("Expresar ideas directamente en código" se lee un poco como el Zen de Python) como una forma de comunicarse.
Wolf
Suplemento: enlace directo a la sección de Filosofía de las Pautas del Código C ++.
Wolf
1

Hay 2 opciones para lidiar con un puntero no válido pasado, primero verificar y regresar temprano o dejar que sea un comportamiento indefinido (si le importa más la velocidad que la robustez).

La comprobación es tan simple como:

void foo(void* buffer){
    if(buffer == nullptr)
        return;

    //actually foo

    // note no increase in indentation required

}

Este tipo de verificación generalmente se acepta como una verificación de parámetros. Si ve el código, está bastante claro que espera que se pase un puntero no nulo y que regrese temprano si no es así. Esto le permite no preocuparse tanto por punteros inválidos.

monstruo de trinquete
fuente
Bueno, pensé en este patrón y lo encuentro absolutamente razonable. Lamentablemente, no parece tan claro como assert(buffer);Sabiendo que la afirmación está activa solo para la versión de depuración, a veces deseo tener una rt_assert(buffer);que arroje una excepción. La sangría de la returnapariencia es un poco peligrosa ... Por cierto: su fragmento de código es una buena ilustración de mi pregunta sobre los punteros para la salida.
Lobo
1

Todo se reduce a tu observación If an output parameter is required.

El único lugar donde se requiere una firma de función para tener un parámetro de salida es cuando es especificada por una API externa, y en tal caso simplemente envuelve la API externa en algo que asegura que siempre haya un puntero válido.

Internamente, evita los parámetros de salida extendiendo el tipo de retorno para que sea un compuesto de todas las "salidas"

Caleth
fuente
¿Quieres decir que el único lugar donde no puedo proporcionar un puntero no obligatorio? Es cierto, pero no estoy seguro de si su regla The only place where...es realmente aplicable a todos los casos. Aspecto que sugiere: evite los parámetros de salida en las funciones de sus propios programas. Es cierto para los nuevos programas.
Wolf
1
Sí, evite los parámetros de salida, prefiera los tipos de retorno compuestos, editados para hacerlo más claro
Caleth