¿No le gusta C ++ cuando se trata de las "características ocultas de" línea de preguntas? Pensé que lo tiraría por ahí. ¿Cuáles son algunas de las características ocultas de C ++?
c++
hidden-features
Craig H
fuente
fuente
Respuestas:
La mayoría de los programadores de C ++ están familiarizados con el operador ternario:
Sin embargo, no se dan cuenta de que se puede usar como un lvalue:
que es la abreviatura de
Úselo con precaución :-)
fuente
(value ? function1 : function2)()
.function1
yfunction2
se convierten implícitamente en punteros de función, y el resultado se convierte implícitamente de nuevo.Puede poner URI en la fuente C ++ sin errores. Por ejemplo:
fuente
goto
, que C ++ sí tiene). Todo lo que siga a dos barras es un comentario. Por lo tanto, conhttp://stackoverflow.com
,http
es una etiqueta (en teoría, podría escribirgoto http;
) y//stackoverflow.com
es solo un comentario de final de línea. Ambos son C ++ legal, por lo que la construcción se compila. No hace nada vagamente útil, por supuesto.goto http;
, en realidad no sigue la URL. :(Los programadores de C ++ prefieren evitar los punteros debido a los errores que pueden introducirse.
¿Sin embargo, el C ++ más genial que he visto? Literales analógicos.
fuente
Estoy de acuerdo con la mayoría de las publicaciones: C ++ es un lenguaje de múltiples paradigmas, por lo que las características "ocultas" que encontrará (aparte de los "comportamientos indefinidos" que debe evitar a toda costa) son usos inteligentes de las instalaciones.
La mayoría de esas instalaciones no son características integradas del lenguaje, sino que están basadas en bibliotecas.
El más importante es el RAII , a menudo ignorado durante años por los desarrolladores de C ++ que vienen del mundo C. La sobrecarga del operador es a menudo una característica mal entendida que permite tanto un comportamiento similar a una matriz (operador de subíndice), operaciones similares a punteros (punteros inteligentes) y operaciones similares a incorporadas (multiplicar matrices.
El uso de la excepción es a menudo difícil, pero con algo de trabajo, puede producir un código realmente robusto a través de especificaciones de seguridad de excepción (incluido el código que no fallará o que tendrá características similares a las de confirmación que tendrán éxito o volverán a su estado original).
La característica "oculta" más famosa de C ++ es la metaprogramación de plantillas , ya que le permite ejecutar su programa parcial (o totalmente) en tiempo de compilación en lugar de en tiempo de ejecución. Sin embargo, esto es difícil y debe tener un conocimiento sólido de las plantillas antes de probarlo.
Otros hacen uso del paradigma múltiple para producir "formas de programación" fuera del antepasado de C ++, es decir, C.
Mediante el uso de functores , puede simular funciones, con seguridad de tipo adicional y estado. Usando el patrón de comando , puede retrasar la ejecución del código. La mayoría de los otros patrones de diseño se pueden implementar fácil y eficientemente en C ++ para producir estilos de codificación alternativos que no se supone que estén dentro de la lista de "paradigmas oficiales de C ++".
Mediante el uso de plantillas , puede producir código que funcionará en la mayoría de los tipos, incluido el que pensó al principio. También puede aumentar la seguridad de los tipos (como un malloc / realloc / free seguro de tipos automatizado). Las características de los objetos de C ++ son realmente poderosas (y, por lo tanto, peligrosas si se usan sin cuidado), pero incluso el polimorfismo dinámico tiene su versión estática en C ++: el CRTP .
He descubierto que la mayoría de los libros del tipo " C ++ eficaz " de Scott Meyers o los libros del tipo " C ++ excepcional " de Herb Sutter son fáciles de leer y constituyen un tesoro de información sobre las características conocidas y menos conocidas de C ++.
Entre mis preferidos se encuentra uno que debería hacer que el cabello de cualquier programador de Java se levante del horror: en C ++, la forma más orientada a objetos de agregar una característica a un objeto es a través de una función no miembro no amiga, en lugar de un miembro- función (es decir, método de clase), porque:
En C ++, la interfaz de una clase son sus funciones miembro y las funciones no miembros en el mismo espacio de nombres
Las funciones no amigas no miembros no tienen acceso privilegiado a la clase interna. Como tal, el uso de una función miembro sobre una no miembro no amiga debilitará la encapsulación de la clase.
Esto nunca deja de sorprender incluso a los desarrolladores experimentados.
(Fuente: Entre otros, Gurú de la semana en línea de Herb Sutter # 84: http://www.gotw.ca/gotw/084.htm )
fuente
Una característica del idioma que considero algo oculta, porque nunca había oído hablar de ella durante todo mi tiempo en la escuela, es el alias del espacio de nombres. No me llamó la atención hasta que encontré ejemplos en la documentación de boost. Por supuesto, ahora que lo sé, puede encontrarlo en cualquier referencia estándar de C ++.
fuente
using
.No solo se pueden declarar variables en la parte de inicio de un
for
bucle, sino también clases y funciones.Eso permite múltiples variables de diferentes tipos.
fuente
El operador de matriz es asociativo.
A [8] es sinónimo de * (A + 8). Dado que la suma es asociativa, se puede reescribir como * (8 + A), que es sinónimo de ..... 8 [A]
No dijiste útil ... :-)
fuente
A
no importa en absoluto. Por ejemplo, siA
fuera achar*
, el código aún sería válido.Una cosa que se sabe poco es que las uniones también pueden ser plantillas:
Y también pueden tener constructores y funciones miembro. Nada que tenga que ver con la herencia (incluidas las funciones virtuales).
fuente
From
yTo
se configuran y usan en consecuencia. Sin embargo, tal unión se puede usar con un comportamiento definido (To
siendo una matriz de caracteres sin firmar o una estructura que comparte una secuencia inicial conFrom
). Incluso si lo usa de una manera indefinida, aún podría ser útil para trabajos de bajo nivel. De todos modos, este es solo un ejemplo de una plantilla de unión; puede haber otros usos para una unión con plantilla.C ++ es un lenguaje de múltiples paradigmas, puede apostar su último dinero a que haya funciones ocultas. Un ejemplo entre muchos: metaprogramación de plantillas . Nadie en el comité de estándares tenía la intención de que hubiera un sublenguaje completo de Turing que se ejecute en tiempo de compilación.
fuente
Otra característica oculta que no funciona en C es la funcionalidad del unario
+
operador . Puedes usarlo para promover y degradar todo tipo de cosas.Convertir una enumeración en un número entero
Y su valor de enumerador que anteriormente tenía su tipo de enumeración ahora tiene el tipo de entero perfecto que puede ajustarse a su valor. ¡Manualmente, difícilmente conocerías a ese tipo! Esto es necesario, por ejemplo, cuando desea implementar un operador sobrecargado para su enumeración.
Obtener el valor de una variable
Tiene que usar una clase que usa un inicializador estático en su clase sin una definición fuera de clase, pero a veces no se puede vincular. El operador puede ayudar a crear un temporal sin hacer suposiciones o dependencias de su tipo.
Decayar una matriz a un puntero
¿Quiere pasar dos punteros a una función, pero simplemente no funcionará? El operador puede ayudar
fuente
La vida útil de los temporales vinculados a referencias constantes es algo que pocas personas conocen. O al menos es mi pieza favorita de conocimiento de C ++ que la mayoría de la gente no conoce.
fuente
Una buena característica que no se usa con frecuencia es el bloque try-catch de toda la función:
El uso principal sería traducir la excepción a otra clase de excepción y volver a lanzar, o traducir entre excepciones y manejo de código de error basado en retorno.
fuente
return
desde el bloque de captura de Function Try, solo volver a lanzar.Muchos conocen la metafunción
identity
/id
, pero hay un buen caso de uso para los casos que no son de plantilla: Facilidad para escribir declaraciones:¡Ayuda enormemente a descifrar declaraciones de C ++!
fuente
template<typename Ret,typename... Args> using function = Ret (Args...); template<typename T> using pointer = *T;
->pointer<function<void,int>> f(pointer<function<void,void>>);
opointer<void(int)> f(pointer<void()>);
ofunction<pointer<function<void,int>>,pointer<function<void,void>>> f;
Una característica bastante oculta es que puede definir variables dentro de una condición if, y su alcance se extenderá solo sobre los bloques if y else:
Algunas macros usan eso, por ejemplo, para proporcionar un alcance "bloqueado" como este:
También BOOST_FOREACH lo usa debajo del capó. Para completar esto, no solo es posible en un if, sino también en un switch:
y en un bucle while:
(y también en una condición for). Pero no estoy muy seguro de si estos son tan útiles :)
fuente
if((a = f()) == b) ...
, pero esta respuesta en realidad declara una variable en la condición.for(...; int i = foo(); ) ...;
This pasará por el cuerpo siempre quei
sea verdadero, inicializándolo cada vez de nuevo. El ciclo que muestra es simplemente una demostración de una declaración de variable, pero no una declaración de variable que actúa simultáneamente como una condición :)Evitar que el operador de coma llame a las sobrecargas del operador
A veces, hace un uso válido del operador de coma, pero desea asegurarse de que ningún operador de coma definido por el usuario se interponga, porque, por ejemplo, confía en los puntos de secuencia entre el lado izquierdo y el derecho o desea asegurarse de que nada interfiera con el acción. Aquí es donde
void()
entra en juego:Ignore los marcadores de posición que puse para la condición y el código. Lo importante es el
void()
, que hace que el compilador fuerce el uso del operador de coma incorporado. Esto también puede ser útil al implementar clases de rasgos, a veces.fuente
Inicialización de matriz en constructor. Por ejemplo, en una clase si tenemos una matriz de
int
como:Podemos inicializar todos los elementos de la matriz a su valor predeterminado (aquí todos los elementos de la matriz a cero) en el constructor como:
fuente
Oooh, puedo crear una lista de los que odian a las mascotas:
En el lado positivo
fuente
Puede acceder a datos protegidos y miembros de funciones de cualquier clase, sin un comportamiento indefinido y con la semántica esperada. Siga leyendo para ver cómo. Lea también el informe de defectos sobre esto.
Normalmente, C ++ le prohíbe acceder a miembros protegidos no estáticos del objeto de una clase, incluso si esa clase es su clase base
Eso está prohibido: usted y el compilador no saben a qué apunta realmente la referencia. Podría ser un
C
objeto, en cuyo caso la claseB
no tiene nada que ver con sus datos. Dicho acceso solo se concede six
es una referencia a una clase derivada o una derivada de ella. Y podría permitir que un fragmento de código arbitrario lea cualquier miembro protegido simplemente creando una clase "desechable" que lea los miembros, por ejemplostd::stack
:Seguramente, como ve, esto causaría demasiado daño. Pero ahora, los indicadores de miembros permiten eludir esta protección. El punto clave es que el tipo de puntero de miembro está vinculado a la clase que realmente contiene dicho miembro, no a la clase que especificó al tomar la dirección. Esto nos permite eludir la comprobación
Y, por supuesto, también funciona con el
std::stack
ejemplo.Eso será aún más fácil con una declaración using en la clase derivada, que hace que el nombre del miembro sea público y se refiere al miembro de la clase base.
fuente
Otra característica oculta es que puede llamar a objetos de clase que se pueden convertir en punteros de función o referencias. La resolución de sobrecarga se realiza sobre el resultado de ellos y los argumentos se envían perfectamente.
Se denominan "funciones de llamada sustitutas".
fuente
Funciones ocultas:
Si una función lanza una excepción que no figura en sus especificaciones de excepción, pero la función tiene
std::bad_exception
en su especificación de excepción, la excepción se conviertestd::bad_exception
y se lanza automáticamente. De esa manera, al menos sabrá quebad_exception
se lanzó un. Leer más aquí .función prueba bloques
La palabra clave de plantilla para eliminar la ambigüedad de typedefs en una plantilla de clase. Si el nombre de un miembro de especialización de plantilla aparece después de una
.
,->
o::
del operador, y que el nombre tiene parámetros de plantilla expresamente calificados, el prefijo del nombre de la plantilla miembro de la plantilla de palabras clave. Leer más aquí .Los valores predeterminados de los parámetros de función se pueden cambiar en tiempo de ejecución. Leer más aquí .
A[i]
funciona tan bien comoi[A]
Las instancias temporales de una clase se pueden modificar. Se puede invocar una función miembro no constante en un objeto temporal. Por ejemplo:
Leer más aquí .
Si dos tipos diferentes están presentes antes y después de la expresión del operador
:
ternary (?:
), entonces el tipo resultante de la expresión es el más general de los dos. Por ejemplo:fuente
map::operator[]
crea una entrada si falta la clave y devuelve una referencia al valor de entrada construido por defecto. Entonces puedes escribir:Me sorprende la cantidad de programadores de C ++ que no saben esto.
fuente
.find()
.const map::operator[]
genera mensajes de error"Poner funciones o variables en un espacio de nombres sin nombre desaprueba el uso de
static
para restringirlas al alcance del archivo.fuente
static
el alcance global no está desaprobado de ninguna manera. (Para referencia: C ++ 03 §D.2)static
use solo debe usarse dentro de un tipo de clase o función.La definición de funciones de amigos ordinarias en las plantillas de clase necesita una atención especial:
En este ejemplo, dos instancias diferentes crean dos definiciones idénticas: una violación directa de la ODR
Por lo tanto, debemos asegurarnos de que los parámetros de plantilla de la plantilla de clase aparezcan en el tipo de cualquier función amiga definida en esa plantilla (a menos que queramos evitar más de una instanciación de una plantilla de clase en un archivo en particular, pero esto es bastante improbable). Apliquemos esto a una variación de nuestro ejemplo anterior:
Descargo de responsabilidad: he pegado esta sección de C ++ Templates: The Complete Guide / Section 8.4
fuente
las funciones nulas pueden devolver valores nulos
Poco conocido, pero el siguiente código está bien
Así como el siguiente de aspecto extraño
Sabiendo esto, puede aprovechar en algunas áreas. Un ejemplo: las
void
funciones no pueden devolver un valor, pero tampoco puede devolver nada, porque se pueden crear instancias con non-void. En lugar de almacenar el valor en una variable local, lo que provocará un errorvoid
, simplemente devuelva un valor directamentefuente
Leer un archivo en un vector de cadenas:
istream_iterator
fuente
vector<string> V((istream_iterator<string>(cin)), istream_iterator<string>());
- paréntesis faltantes después del segundoPuede crear plantillas de campos de bits.
Todavía no se me ha ocurrido ningún propósito para esto, pero seguro que me sorprendió.
fuente
Una de las gramáticas más interesantes de todos los lenguajes de programación.
Tres de estas cosas van juntas, y dos son algo completamente diferente ...
Todos menos el tercero y el quinto definen un
SomeType
objeto en la pila y lo inicializan (conu
en los dos primeros casos y el constructor predeterminado en el cuarto. El tercero declara una función que no toma parámetros y devuelve aSomeType
. El quinto declara de manera similar una función que toma un parámetro por valor de tipoSomeType
nombradou
.fuente
Deshacerse de las declaraciones futuras:
Escribir sentencias de cambio con operadores?::
Haciendo todo en una sola línea:
Poniendo a cero estructuras sin memset:
Normalizar / envolver valores de ángulo y tiempo:
Asignar referencias:
fuente
FStruct s = {};
es aún más corto.main
? Sugeriríaglobal().main();
y simplemente olvídese del singleton ( puede trabajar con el temporal, lo que prolonga su vida útil )El operador condicional ternario
?:
requiere que su segundo y tercer operando tengan tipos "agradables" (hablando informalmente). Pero este requisito tiene una excepción (juego de palabras): el segundo o tercer operando puede ser una expresión de lanzamiento (que tiene el tipovoid
), independientemente del tipo del otro operando.En otras palabras, uno puede escribir las siguientes expresiones C ++ perfectamente válidas usando el
?:
operadorPor cierto, el hecho de que throw expression sea en realidad una expresión (de tipo
void
) y no una declaración es otra característica poco conocida del lenguaje C ++. Esto significa, entre otras cosas, que el siguiente código es perfectamente válidoaunque no tiene mucho sentido hacerlo de esta manera (tal vez en algún código de plantilla genérico esto pueda ser útil).
fuente
La regla de dominancia es útil, pero poco conocida. Dice que incluso si se encuentra en una ruta no única a través de una red de clase base, la búsqueda de nombre para un miembro parcialmente oculto es única si el miembro pertenece a una clase base virtual:
He utilizado esto para implementar el soporte de alineación que determina automáticamente la alineación más estricta mediante la regla de dominio.
Esto no solo se aplica a las funciones virtuales, sino también a los nombres typedef, miembros estáticos / no virtuales y cualquier otra cosa. Lo he visto utilizado para implementar rasgos sobrescribibles en metaprogramas.
fuente
struct C
en su ejemplo ...? Salud.