guide - ¿En C++ es aceptable "const" después de la ID de tipo?
google code style java (15)
Mi compañero de trabajo es 0 por 2 en preguntas que ha inspirado ( 1 , 2 ), así que pensé que le daría la oportunidad de ponerme al día.
Nuestro último desacuerdo es sobre el tema del estilo donde colocar "const" en las declaraciones.
Es de la opinión de que debería ir delante del tipo o después del puntero. El razonamiento es que esto es lo que normalmente hacen todos los demás, y otros estilos pueden ser confusos. Por lo tanto, un puntero a una constante int, y un puntero constante a int serían respectivamente:
const int *i;
int * const i;
Sin embargo, estoy confundido de todos modos. Necesito reglas que sean consistentes y fáciles de entender, y la única forma en que puedo darle sentido a "const" es que vaya después de lo que está modificando. Hay una excepción que le permite ir delante del tipo final, pero es una excepción, por lo que es más fácil para mí si no lo uso.
Por lo tanto, un puntero a una constante int, y un puntero constante a int serían respectivamente:
int const * i;
int * const i;
Como beneficio adicional, hacer las cosas de esta manera hace que los niveles más profundos de indirección sean más fáciles de entender. Por ejemplo, un puntero a un puntero constante a int sería claramente:
int * const * i;
Mi opinión es que si alguien simplemente lo aprende a su manera, no tendrá ningún problema en descubrir qué es lo que funciona arriba.
El último problema aquí es que él piensa que poner const después de int es tan indescriptiblemente feo y tan dañino para la legibilidad que debería estar prohibido en la guía de estilo. Por supuesto, creo que en todo caso la guía debería sugerir hacerlo a mi manera, pero de cualquier manera no deberíamos prohibir un enfoque.
Edit: He recibido muchas respuestas buenas, pero ninguna de ellas aborda directamente mi último párrafo ("El último problema"). Mucha gente defiende la coherencia, pero ¿es eso tan deseable en este caso que es una buena idea prohibir la otra forma de hacerlo, en lugar de desalentarlo?
El último problema aquí es que él piensa que poner const después de int es tan indescriptiblemente feo y tan dañino para la legibilidad que debería estar prohibido en la guía de estilo.
De Verdad?
Muéstrame un programador que se atasca cuando ve:
int foo() {
}
vs
int foo()
{
}
... y te mostraré un programador que no presta suficiente atención a los detalles.
Ningún programador profesional que valga su pena tendrá problemas con diferencias superficiales de estilo.
EDITAR: Es cierto que const int * y int * const no significan exactamente lo mismo, pero ese no era el punto. El punto señalado por el compañero de trabajo de OP fue que las diferencias de estilo hacen que el código sea difícil de entender y mantener. No estoy de acuerdo con esta afirmación.
Déjame poner mis 2 ¢ en la discusión.
En la constexpr
moderna de C ++, recomiendo mantener int const
lugar de const int
para resaltar la diferencia entre const
y constexpr
. Sería útil para el programador recordar que no siempre se puede pensar en const int
como una constante de compilación, porque:
- el compilador puede (¿lo hará?) asignar memoria para ello,
- esta memoria puede incluso no estar protegida contra escritura (segmento de pila),
- tal constante no puede servir a todos los deberes
constexpr
puede .
En C ++ 11 moderno, también podría usar typedefs de plantillas para agregar claridad a declaraciones complicadas:
template<typename T>
using Const = const T;
template<typename T>
using Ptr = T*;
Las tres declaraciones que mencionaste en tu pregunta:
int const * a;
int * const b;
int * const * c;
Entonces se escribiría como:
Ptr<Const<int>> a;
Const<Ptr<int>> b;
Ptr<Const<Ptr<int>>> c;
Espero que esta explicación de las Preguntas frecuentes sobre estilo y técnicas de B. Stroustrup le brinde una respuesta definitiva.
Preguntas frecuentes sobre el estilo y la técnica en C ++ de Bjarne Stroustrup
Personalmente prefiero:
int const* pi;
int* const pi;
Porque const
identifica el token izquierdo que se pretende que sea const.
Y definitivamente mantienes la misma consistencia cuando usas algo así:
int const* const pi;
En lugar de escribir de manera inconsistente:
const int* const pi;
Y qué pasa si tienes un puntero para apuntar y así sucesivamente:
int const* const* const pi;
En lugar de:
const int* const* const pi;
Estoy de acuerdo con los dos. Debes poner la const después del tipo. También me parece que verlo es una abominación que debe ser destruida. Pero mi reciente incursión en las maravillas de los parámetros de valores const me ha hecho entender por qué poner en orden a la constante tiene sentido.
int *
int const *
int * const
int const * const
Solo mirando eso tiene los pelos en mi cuello parados. Estoy seguro de que confundiría a mis compañeros de trabajo.
EDITAR: Me preguntaba acerca de cómo usar esto en las clases:
class Foo {
Bar* const bar;
Foo(const Foo&) = delete; // would cause too many headaches
public:
Foo() : bar(new Bar) {}
~Foo() { delete bar; }
};
bar
en este ejemplo es funcionalmente equivalente a Bar&
está en el montón y se puede eliminar. Durante la vida útil de cada Foo, habrá una única barra asociada a él.
Estuve en una conferencia donde Bjarne Stroustrup estaba dando una presentación, y él usó algo como const int * i; Alguien le preguntó por qué este estilo y él respondió (parafraseando): "a la gente le gusta ver primero a Const cuando algo es constante".
Hay una clase de ejemplos donde poner la constante a la derecha del tipo también ayuda a evitar confusiones.
Si tiene un tipo de puntero en un typedef, entonces no es posible cambiar la constancia del tipo a:
typedef int * PINT;
const PINT pi;
''pi'' todavía tiene el tipo ''int * const'', y este es el mismo, no importa donde escriba ''const''.
La gente suele usar const int* blah
porque se lee bien en inglés. No subestimaría la utilidad de eso.
Encuentro que la variación int* const blah
es lo suficientemente rara como para que no sea útil para hacer la definición más común hacia atrás. En general, no soy un fanático de nada que incluso oculte un poco el código en el caso general, aunque podría proporcionar algún beneficio nominal en el caso excepcional.
Véase también "si (1 == a)". Algunas personas realmente disfrutan escribiendo código que no se lee en inglés. No soy una de esas personas.
Realmente, sin embargo, las reglas detrás de const son simples. Mira a la izquierda, luego a la derecha. Tan simple que no creo que valga la pena prestar atención en una guía de estilo.
Las reglas son buenas para seguir. Las reglas más simples son mejores. Const va a la derecha de lo que es const.
Toma esta declaración:
int main (int const argc, char const * const * const argv) ...
Lo más importante es la consistencia . Si no hay pautas de codificación para esto, entonces elige una y apégate a ella. Pero, si su equipo ya tiene un estándar de facto, ¡no lo cambie!
Dicho esto, creo que, por mucho, lo más común es
const int* i;
int* const j;
porque la mayoría de la gente escribe
const int n;
en lugar de
int const n;
Una nota al margen: una forma fácil de leer la const
puntero es leer la declaración comenzando por la derecha.
const int* i; // pointer to an int that is const
int* const j; // constant pointer to a (non-const) int
int const* aLessPopularWay; // pointer to a const int
Me gusta usar el siguiente formulario para declarar "constantes manifiestas" . En este caso, el valor en sí mismo es una constante, así que coloco la "const" primero ( igual que Bjarne ) para enfatizar que la constancia debe manifestarse en tiempo de compilación, y se puede usar como tal para optimizaciones específicas por parte del compilador.
const int i = 123;
Para declarar referencias que no se utilizarán para modificar el valor, utilizo la siguiente forma que enfatiza el hecho de que el identificador es una "referencia constante". El valor referenciado puede o no ser una constante. [Discusión relacionada: ¿Estaría usando "const" para los parámetros de la función si no fueran punteros o referencias? Uso de ''const'' para parámetros de función ]
void fn( int const & i );
Para los punteros, uso la misma forma que uso para las referencias, esencialmente por la misma razón (aunque el término "puntero constante" parece un poco más ambiguo que la "referencia constante").
void fn( int const * i );
Además, como se señaló en otro póster, este formulario permanece constante cuando tiene múltiples niveles de direccionamiento indirecto.
void fn( int const * const * i );
El escenario final, en el que está declarando un puntero que es constante es bastante raro en mi experiencia con C ++. En cualquier caso, realmente no tienes opciones aquí. [Este caso demuestra que el enfoque más consistente sería colocar la palabra "const" después del tipo, ya que, de hecho, es necesario para esta declaración en particular.]
void fn( int * const i );
... a menos que uses un typedef.
typedef int * IntPtr;
void fn1( const IntPtr i );
void fn2( IntPtr const i );
Una nota final: a menos que esté trabajando en un dominio de bajo nivel, la mayoría del código C ++ nunca debe declarar un puntero . Por lo tanto, ese aspecto de esta discusión es probablemente más relevante para C.
Personalmente I (y es una preferencia personal) I encuentra declaraciones de tipo de lectura de derecha a izquierda lo más fácil. Especialmente cuando comienzas a lanzar referencias en la mezcla.
std::string const& name = plop; // reference to const string.
const std::string& flame =plop; // reference to string const;
// That works better if you are German I suppose :-)
Poner "const" después de la declaración de tipo tiene mucho más sentido una vez que te entrenas para leer tus declaraciones de tipo C ++ de derecha a izquierda.
Voy a pegar su orador de vaca a 0-por-3 :)
Si bien no hay una diferencia significativa entre const int
y int const
(y he visto ambos estilos en uso), hay una diferencia entre const int *
y int * const
.
En la primera, tienes un puntero a un int. Puede cambiar el puntero, pero no puede cambiar el valor al que apunta. En el segundo, tienes un puntero constante a int. No puede cambiar el puntero (espero que se haya inicializado a su gusto), pero puede cambiar el valor de int apuntado a.
La comparación adecuada es con const int *
y int const *
, que ambos son punteros a una const. Int.
Recuerda que el *
no funciona necesariamente como te gustaría. La declaración int x, y;
funcionará como esperas, pero int* x, y;
declara un puntero a int, y uno int.
Si solo las variables y los punteros a ellos pudieran ser const
o no, probablemente no importaría mucho. Pero considere:
class MyClass
{
public:
int foo() const;
};
No hay forma de que const
pueda escribir en ningún otro lugar, excepto la función a la que se refiere.
Cuanto más corta sea una guía de estilo, más probable será que los desarrolladores la sigan. Y la regla más corta posible, y la única regla que le dará consistencia , es:
La palabra clave const
siempre sigue a lo que se refiere.
Por lo tanto, diría 0: 3 para su compañero de trabajo aquí.
Con respecto a su "último problema": por el bien de la guía de estilo, no importa si la guía "desalienta" o "prohíbe" las cosas contra las que habla. Eso es una cuestión social, una política. La guía de estilo en sí debe ser lo más nítida y corta posible. Las guías de estilo largo solo pueden ser ignoradas por todos (excepto la gerencia en busca de alguien a quien culpar), así que simplemente escriba "hacer" o "no hacer", e indique qué hace con las violaciones de la guía en otro lugar (por ejemplo, en la política de la compañía de cómo se hacen las revisiones por pares).