Uso de ‘const’ para los parámetros de función

¿Qué tan lejos vas con const ? ¿Hace que las funciones se const cuando sea necesario o va por completo y lo usa en todas partes? Por ejemplo, imagina un simple mutador que toma un solo parámetro booleano:

 void SetValue(const bool b) { my_val_ = b; } 

¿Es eso const realmente útil? Personalmente, opto por usarlo extensivamente, incluidos los parámetros, pero en este caso me pregunto si vale la pena.

También me sorprendió saber que puede omitir const de parámetros en una statement de función, pero puede incluirlo en la definición de la función, por ejemplo:

archivo .h

 void func(int n, long l); 

archivo .cpp

 void func(const int n, const long l) 

¿Hay alguna razón para esto? Me parece un poco inusual

La razón es que const para el parámetro solo se aplica localmente dentro de la función, ya que está trabajando en una copia de los datos. Esto significa que la firma de la función es realmente la misma de todos modos. Sin embargo, es probablemente un mal estilo hacer esto mucho.

Personalmente, no uso const excepto los parámetros de referencia y puntero. Para los objetos copiados, realmente no importa, aunque puede ser más seguro ya que indica la intención dentro de la función. Es realmente una llamada de juicio. Aunque tiendo a usar const_iterator cuando bucleo algo y no tengo la intención de modificarlo, supongo que cada uno es suyo, siempre y cuando la exactitud de la constidad para los tipos de referencia se mantenga rigurosamente.

“const no tiene sentido cuando el argumento pasa por valor ya que no se modificará el objeto del que llama”.

Incorrecto.

Se trata de auto-documentar su código y sus suposiciones.

Si su código tiene mucha gente trabajando en él y sus funciones no son triviales, debe marcar “const” y todo lo que pueda. Al escribir código de fuerza industrial, siempre debes asumir que tus compañeros de trabajo son psicópatas que intentan atraparte de cualquier manera posible (especialmente porque a menudo eres tú mismo en el futuro).

Además, como alguien mencionó anteriormente, podría ayudar al comstackdor a optimizar un poco las cosas (aunque es una posibilidad remota).

A veces (¡con demasiada frecuencia!) Tengo que desenredar el código C ++ de otra persona. Y todos sabemos que el código C ++ de otra persona es un desastre completo casi por definición 🙂 Así que lo primero que hago para descifrar el flujo de datos locales es poner const en cada definición de variable hasta que el comstackdor comience a ladrar. Esto también significa argumentos const-qualifying value, porque son simplemente variables locales de fantasía inicializadas por la persona que llama.

Ah, desearía que las variables fueran const de manera predeterminada y mutable fuera necesario para las variables que no son const 🙂

Las siguientes dos líneas son funcionalmente equivalentes:

 int foo (int a); int foo (const int a); 

Obviamente, no podrás modificar a en el cuerpo de foo si está definido de la segunda manera, pero no hay diferencia desde el exterior.

Donde const realmente es útil es con referencia o parámetros de puntero:

 int foo (const BigStruct &a); int foo (const BigStruct *a); 

Lo que esto dice es que foo puede tomar un parámetro grande, tal vez una estructura de datos que tenga un tamaño de gigabytes, sin copiarlo. Además, le dice a la persona que llama, “Foo no * cambiará el contenido de ese parámetro”. Pasar una referencia constante también permite al comstackdor tomar ciertas decisiones de rendimiento.

*: A menos que descarte la const-ness, pero esa es otra publicación.

Const extrafluo son malos desde el punto de vista API:

Poner const’s extra superfluas en su código para los parámetros de tipo intrínseco pasados ​​por valor desordenan su API sin hacer una promesa significativa para la persona que llama o el usuario de la API (solo obstaculiza la implementación).

Demasiadas ‘const’ en una API cuando no se necesitan es como ” lobo que llora “, eventualmente la gente comenzará a ignorar ‘const’ porque está por todas partes y no significa nada la mayor parte del tiempo.

El argumento de “reductio ad absurdum” a consensos adicionales en API es bueno para estos dos primeros puntos si es que más parámetros de const son buenos, entonces cada argumento que pueda tener una const, DEBERÍA tener una const. De hecho, si fuera realmente así de bueno, querría que const fuera el predeterminado para los parámetros y tenga una palabra clave como “mutable” solo cuando desee cambiar el parámetro.

Así que intentemos poner en const siempre que podamos:

 void mungerum(char * buffer, const char * mask, int count); void mungerum(char * const buffer, const char * const mask, const int count); 

Considera la línea de código anterior. No solo es la statement más desordenada, más larga y más difícil de leer, sino que tres de las cuatro palabras clave “const” pueden ser ignoradas por el usuario API. Sin embargo, el uso adicional de ‘const’ ha hecho que la segunda línea sea potencialmente PELIGROSA.

¿Por qué?

Una rápida lectura incorrecta del primer parámetro char * const buffer podría hacerle pensar que no modificará la memoria en el búfer de datos que se transfiere; sin embargo, esto no es cierto. La ‘const’ superflua puede llevar a suposiciones peligrosas e incorrectas sobre su API cuando se escanea o se lee mal.


Const superfluous son malos desde el punto de vista de implementación del código también:

 #if FLEXIBLE_IMPLEMENTATION #define SUPERFLUOUS_CONST #else #define SUPERFLUOUS_CONST const #endif void bytecopy(char * SUPERFLUOUS_CONST dest, const char *source, SUPERFLUOUS_CONST int count); 

Si FLEXIBLE_IMPLEMENTATION no es verdadero, entonces la API “promete” no implementar la función de la siguiente manera.

 void bytecopy(char * SUPERFLUOUS_CONST dest, const char *source, SUPERFLUOUS_CONST int count) { // Will break if !FLEXIBLE_IMPLEMENTATION while(count--) { *dest++=*source++; } } void bytecopy(char * SUPERFLUOUS_CONST dest, const char *source, SUPERFLUOUS_CONST int count) { for(int i=0;i 

Esa es una promesa muy tonta de hacer. ¿Por qué debería hacer una promesa que no le da ningún beneficio a la persona que llama y solo limita su implementación?

Ambas son implementaciones perfectamente válidas de la misma función, así que todo lo que has hecho es atar una mano detrás de tu espalda innecesariamente.

Además, es una promesa muy superficial que es fácil (y eludida legalmente).

 inline void bytecopyWrapped(char * dest, const char *source, int count) { while(count--) { *dest++=*source++; } } void bytecopy(char * SUPERFLUOUS_CONST dest, const char *source,SUPERFLUOUS_CONST int count) { bytecopyWrapped(dest, source, count); } 

Mira, lo implementé de esa manera de todos modos, aunque prometí no hacerlo, simplemente usando una función de envoltura. Es como cuando el malo promete no matar a alguien en una película y ordena a su secuaz que los mate en su lugar.

Esos const's superfluos no valen más que una promesa de un malo de la película.


Pero la capacidad de mentir empeora aún más:

Me han aclarado que puedes hacer una coincidencia const en el encabezado (statement) y el código (definición) al usar const const. Los defensores de const-happy afirman que esto es algo bueno ya que le permite poner const solo en la definición.

 // Example of const only in definition, not declaration class foo { void test(int *pi); }; void foo::test(int * const pi) { } 

Sin embargo, lo contrario es verdad ... puedes poner una constante espuria solo en la statement e ignorarla en la definición. Esto solo hace que la const suplfl en una API sea más una cosa terrible y una mentira horrible - mira este ejemplo:

 class foo { void test(int * const pi); }; void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here { pi++; // I promised in my definition I wouldn't modify this } 

Lo único que hace const lo superfluo es hacer que el código del implementador sea menos legible forzándolo a usar otra copia local o una función de envoltura cuando quiere cambiar la variable o pasar la variable por referencia no const.

Mira este ejemplo. ¿Cuál es más legible? ¿Es obvio que la única razón para la variable adicional en la segunda función es porque algún diseñador de API arrojó una const superflua?

 struct llist { llist * next; }; void walkllist(llist *plist) { llist *pnext; while(plist) { pnext=plist->next; walk(plist); plist=pnext; // This line wouldn't compile if plist was const } } void walkllist(llist * SUPERFLUOUS_CONST plist) { llist * pnotconst=plist; llist *pnext; while(pnotconst) { pnext=pnotconst->next; walk(pnotconst); pnotconst=pnext; } } 

Espero haber aprendido algo aquí. Const superfluo es una monstruosidad que satura la API, un fastidio molesto, una promesa superficial y sin sentido, un obstáculo innecesario, y en ocasiones conduce a errores muy peligrosos.

const debería haber sido el predeterminado en C ++. Me gusta esto :

 int i = 5 ; // i is a constant var int i = 5 ; // i is a real variable 

Cuando codifiqué C ++ para vivir, consideré todo lo que pude. Usar const es una excelente manera de ayudar al comstackdor a ayudarte. Por ejemplo, la creación de los valores de retorno de su método puede salvarlo de errores tipográficos como:

 foo() = 42 

cuando quisiste decir:

 foo() == 42 

Si foo () está definido para devolver una referencia no constante:

 int& foo() { /* ... */ } 

El comstackdor le dejará asignar un valor al temporal anónimo devuelto por la llamada a la función. Haciéndolo const:

 const int& foo() { /* ... */ } 

Elimina esta posibilidad.

Hay una buena discusión sobre este tema en los antiguos artículos de “Gurú de la semana” en comp.lang.c ++ moderados aquí .

El artículo GOTW correspondiente está disponible en el sitio web de Herb Sutter aquí .

Uso const en parámetros de función que son referencias (o punteros) que son solo datos [en] y no serán modificados por la función. Es decir, cuando el propósito de usar una referencia es evitar copiar datos y no permitir el cambio del parámetro pasado.

Poner const en el parámetro b booleano en su ejemplo solo impone una restricción a la implementación y no contribuye a la interfaz de la clase (aunque generalmente no se recomienda cambiar los parámetros).

La firma de la función para

 void foo(int a); 

y

 void foo(const int a); 

es lo mismo, lo que explica tu .c y .h

Asaf

Digo const tus parámetros de valor.

Considere esta función con errores:

 bool isZero(int number) { if (number = 0) // whoops, should be number == 0 return true; else return false; } 

Si el parámetro numérico era const, el comstackdor se detendría y nos avisaría del error.

Si usa los operadores ->* o .* , Es obligatorio.

Le impide escribir algo así como

 void foo(Bar *p) { if (++p->*member > 0) { ... } } 

que casi lo hago ahora, y que probablemente no hace lo que pretende.

Lo que quise decir fue

 void foo(Bar *p) { if (++(p->*member) > 0) { ... } } 

y si hubiera puesto un const entre Bar * p , el comstackdor me habría dicho eso.

Ah, una difícil. Por un lado, una statement es un contrato y realmente no tiene sentido pasar un argumento constante por valor. Por otro lado, si nos fijamos en la implementación de la función, le da al comstackdor más oportunidades de optimizar si declara un argumento constante.

const no tiene sentido cuando el argumento se pasa por valor ya que no se modificará el objeto de la persona que llama.

const debe preferirse al pasar por referencia, a menos que el propósito de la función sea modificar el valor pasado.

Finalmente, una función que no modifica el objeto actual (esto) puede, y probablemente debería declararse const. Un ejemplo es a continuación:

 int SomeClass::GetValue() const {return m_internalValue;} 

Esta es una promesa de no modificar el objeto al que se aplica esta llamada. En otras palabras, puedes llamar:

 const SomeClass* pSomeClass; pSomeClass->GetValue(); 

Si la función no era const, esto daría lugar a una advertencia del comstackdor.

Marcar los parámetros de valor ‘const’ es definitivamente algo subjetivo.

Sin embargo, en realidad prefiero marcar los parámetros de valor const, al igual que en su ejemplo.

 void func(const int n, const long l) { /* ... */ } 

El valor para mí es indicar claramente que la función nunca cambia los valores de los parámetros de la función. Tendrán el mismo valor al principio que al final. Para mí, es parte de mantener un tipo de estilo de progtwigción muy funcional.

Para una función corta, es discutible una pérdida de tiempo / espacio para tener el ‘const’ allí, ya que generalmente es bastante obvio que los argumentos no son modificados por la función.

Sin embargo, para una función más grande, es una forma de documentación de implementación y es ejecutada por el comstackdor.

Puedo estar seguro de que si realizo algunos cálculos con ‘n’ y ‘l’, puedo refactorizar / mover ese cálculo sin temor a obtener un resultado diferente porque me perdí un lugar donde uno o ambos cambian.

Como se trata de un detalle de implementación, no necesita declarar los parámetros de valor const en el encabezado, al igual que no necesita declarar los parámetros de la función con los mismos nombres que usa la implementación.

En el caso que mencionas, no afecta a las personas que llaman a tu API, por lo que no se hace comúnmente (y no es necesario en el encabezado). Solo afecta la implementación de su función.

No es particularmente malo hacerlo, pero los beneficios no son tan buenos, ya que no afecta tu API y agrega tipeo, por lo que generalmente no se hace.

No uso const para parametere pasado por valor. A la persona que llama no le importa si modifica el parámetro o no, es un detalle de implementación.

Lo que es realmente importante es marcar los métodos como const si no modifican su instancia. Haga esto sobre la marcha, porque de lo contrario podría terminar con muchos const_cast <> o podría encontrar que marcar un método const requiere cambiar una gran cantidad de código porque llama a otros métodos que deberían haber sido marcados const.

También tiendo a marcar const de vars local si no necesito modificarlos. Creo que hace que el código sea más fácil de entender al facilitar la identificación de las “partes móviles”.

Tiendo a usar const siempre que sea posible. (U otra palabra clave apropiada para el idioma de destino). Hago esto solo porque permite al comstackdor realizar optimizaciones adicionales que de otro modo no podría realizar. Como no tengo idea de qué pueden ser estas optimizaciones, siempre lo hago, incluso cuando parece tonto.

Por lo que sé, el comstackdor podría ver muy bien un parámetro de valor const, y decir: “Oye, esta función no lo está modificando de todos modos, así que puedo pasar por referencia y guardar algunos ciclos de reloj”. No creo que alguna vez hiciera algo así, ya que cambia la firma de la función, pero lo demuestra. Tal vez se trata de una manipulación diferente de la stack o algo … El punto es que no sé, pero sí sé que tratar de ser más inteligente que el comstackdor solo me lleva a avergonzarme.

C ++ tiene algo de equipaje adicional, con la idea de const-correctness, por lo que se vuelve aún más importante.

Yo uso const si puedo. Const para parámetros significa que no deberían cambiar su valor. Esto es especialmente valioso cuando se pasa por referencia. const para la función declara que la función no debe cambiar los miembros de las clases.

Puede ser que esto no sea un argumento válido. pero si incrementamos el valor de una variable const dentro de una función, el comstackdor nos dará un error: ” error: incremento del parámetro de solo lectura “. así que eso significa que podemos usar la palabra clave const como una forma de evitar la modificación accidental de nuestras variables dentro de las funciones (que no se supone que debemos / leer solo). entonces, si accidentalmente lo hicimos en comstackción, el comstackdor nos lo informará. esto es especialmente importante si usted no es el único que está trabajando en este proyecto.

El parámetro Const es útil solo cuando el parámetro se pasa por referencia, es decir, referencia o puntero. Cuando el comstackdor ve un parámetro const, se asegura de que la variable utilizada en el parámetro no se modifique dentro del cuerpo de la función. ¿Por qué alguien querría hacer un parámetro by-value como constante? 🙂

Si el parámetro se pasa por valor (y no es una referencia), generalmente no hay mucha diferencia si el parámetro se declara como const o no (a menos que contenga un miembro de referencia, no es un problema para los tipos incorporados). Si el parámetro es una referencia o un puntero, generalmente es mejor proteger la memoria referenciada / apuntada, no el puntero en sí (creo que no puede hacer que la referencia sea const, no es tan importante ya que no puede cambiar el árbitro) . Parece una buena idea proteger todo lo que puedas como const. Puede omitirlo sin temor a equivocarse si los parámetros son solo POD (incluidos los tipos incorporados) y no hay posibilidad de que cambien más adelante en la carretera (por ejemplo, en el ejemplo del parámetro bool).

No sabía acerca de la diferencia de statement del archivo .h / .cpp, pero tiene cierto sentido. En el nivel de código de máquina, nada es “const”, por lo que si declara una función (en .h) como no const, el código es el mismo que si lo declara como const (optimizaciones a un lado). Sin embargo, te ayuda a alistar el comstackdor para que no cambies el valor de la variable dentro de la implementación de la función (.ccp). Puede ser útil en el caso cuando está heredando de una interfaz que permite cambios, pero no necesita cambiar al parámetro para lograr la funcionalidad requerida.

Sobre las optimizaciones del comstackdor: http://www.gotw.ca/gotw/081.htm

Para resumir:

  • “Normalmente, const pass-by-value es inútil y engañoso en el mejor de los casos”. Desde GOTW006
  • Pero puede agregarlos en .cpp como lo haría con las variables.
  • Tenga en cuenta que la biblioteca estándar no usa const. Ej std::vector::at(size_type pos) . Lo que es bueno para la biblioteca estándar es bueno para mí.

No me gustaría establecer parámetros como ese: todos ya saben que un booleano (a diferencia de un booleano &) es constante, por lo que agregarlo hará que la gente piense “espere, ¿qué?” o incluso que está pasando el parámetro por referencia.

Lo que hay que recordar con const es que es mucho más fácil hacer las cosas const desde el principio, que intentar ponerlas más tarde.

Usa const cuando quieras que algo no cambie: es una pista añadida que describe lo que hace tu función y qué esperar. ¡He visto muchas API de C que podrían funcionar con algunas de ellas, especialmente aquellas que aceptan cadenas c!

Me inclinaría más a omitir la palabra clave const en el archivo cpp que el encabezado, pero como tiendo a cortar y pegar, se mantendrían en ambos lugares. No tengo idea de por qué el comstackdor lo permite, supongo que es algo del comstackdor. La mejor práctica es definitivamente poner su palabra clave const en ambos archivos.

Realmente no hay razón para hacer un parámetro de valor “const” ya que la función solo puede modificar una copia de la variable de todos modos.

The reason to use “const” is if you’re passing something bigger (eg a struct with lots of members) by reference, in which case it ensures that the function can’t modify it; or rather, the compiler will complain if you try to modify it in the conventional way. It prevents it from being accidentally modified.

As parameters are being passed by value,it doesnt make any difference if you specify const or not from the calling function’s perspective.It basically does not make any sense to declare pass by value parameters as const.

All the consts in your examples have no purpose. C++ is pass-by-value by default, so the function gets copies of those ints and booleans. Even if the function does modify them, the caller’s copy is not affected.

So I’d avoid extra consts because

  • They’re redudant
  • They clutter up the text
  • They prevent me from changing the passed in value in cases where it might be useful or efficient.

I know the question is “a bit” outdated but as I came accross it somebody else may also do so in future… …still I doubt the poor fellow will list down here to read my comment 🙂

It seems to me that we are still too confined to C-style way of thinking. In the OOP paradigma we play around with objects, not types. Const object may be conceptually different from a non-const object, specifically in the sense of logical-const (in contrast to bitwise-const). Thus even if const correctness of function params is (perhaps) an over-carefulness in case of PODs it is not so in case of objects. If a function works with a const object it should say so. Consider the following code snippet

 #include  //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class SharedBuffer { private: int fakeData; int const & Get_(int i) const { std::cout < < "Accessing buffer element" << std::endl; return fakeData; } public: int & operator[](int i) { Unique(); return const_cast(Get_(i)); } int const & operator[](int i) const { return Get_(i); } void Unique() { std::cout < < "Making buffer unique (expensive operation)" << std::endl; } }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ void NonConstF(SharedBuffer x) { x[0] = 1; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ void ConstF(const SharedBuffer x) { int q = x[0]; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ int main() { SharedBuffer x; NonConstF(x); std::cout << std::endl; ConstF(x); return 0; } 

ps.: you may argue that (const) reference would be more appropriate here and gives you the same behaviour. Well, right. Just giving a different picture from what I could see elsewhere...

Being a VB.NET programmer that needs to use a C++ program with 50+ exposed functions, and a .h file that sporadically uses the const qualifier, it is difficult to know when to access a variable using ByRef or ByVal.

Of course the program tells you by generating an exception error on the line where you made the mistake, but then you need to guess which of the 2-10 parameters is wrong.

So now I have the distasteful task of trying to convince a developer that they should really define their variables (in the .h file) in a manner that allows an automated method of creating all of the VB.NET function definitions easily. They will then smugly say, “read the … documentation.”

I have written an awk script that parses a .h file, and creates all of the Declare Function commands, but without an indicator as to which variables are R/O vs R/W, it only does half the job.

EDITAR:

At the encouragement of another user I am adding the following;

Here is an example of a (IMO) poorly formed .h entry;

 typedef int (EE_STDCALL *Do_SomethingPtr)( int smfID, const char* cursor_name, const char* sql ); 

The resultant VB from my script;

  Declare Function Do_Something Lib "SomeOther.DLL" (ByRef smfID As Integer, ByVal cursor_name As String, ByVal sql As String) As Integer 

Note the missing “const” on the first parameter. Without it, a program (or another developer) has no Idea the 1st parameter should be passed “ByVal.” By adding the “const” it makes the .h file self documenting so that developers using other languages can easily write working code.