verbos transitivos transitivas tacito son significado requisitos references qué que plus oraciones los lista librerias library jurisprudencia italiano intransitivos intransitivas intransitiva finalidad expreso ejemplos directo cual cpp copulativos contrato consentimiento complemento colombia codigo civil apis c++ header include c++17

c++ - transitivos - #Incluir directo explícito vs.#incluido transitivo no contractual



verbos transitivos e intransitivos pdf (6)

para evitar una dependencia en el funcionamiento interno de MyClass. ¿O debería?

Sí, deberías y por mucho por esa razón. A menos que desee especificar que MyClass.hpp está garantizado para incluir <vector> , no puede confiar en uno, incluido el otro. Y no hay ninguna buena razón para ser forzado a proporcionar dicha garantía. Si no existe tal garantía, entonces confía en un detalle de implementación de MyClass.hpp que puede cambiar en el futuro, lo que romperá su código.

Obviamente me doy cuenta de que MyClass necesita vectores para trabajar.

¿Lo hace? ¿No podría usar, por ejemplo, boost::container::small_vector ?

En este ejemplo, MyClass necesita std :: vector

Pero ¿qué pasa con las necesidades de MyClass en el futuro? Los programas evolucionan, y lo que una clase necesita hoy no es siempre lo mismo que la clase necesita mañana.

Pero, ¿no sería bueno poder decidir qué encabezados quedan expuestos al importar?

La prevención de la inclusión transitiva no es posible.

Los módulos introducidos en C ++ 20 son una característica que se puede usar en lugar de pp-inclusión y están pensados ​​para ayudar a resolver esto.

En este momento, puede evitar incluir dependencias de detalles de implementación utilizando el patrón PIMPL ("Puntero a implementación"). Pero PIMPL introduce una capa de direccionamiento indirecto y, lo que es más importante, requiere una asignación dinámica que tenga implicaciones de rendimiento. Dependiendo del contexto, estas implicaciones pueden ser insignificantes o significativas.

Digamos que tenemos este archivo de encabezado:

MyClass.hpp

#pragma once #include <vector> class MyClass { public: MyClass(double); /* ... */ private: std::vector<double> internal_values; };

Ahora, cuando usamos #include "MyClass.hpp" en algún otro archivo hpp o cpp, efectivamente también #include <vector> , a pesar del hecho de que no lo necesitamos. La razón por la que digo que no es necesario es que std::vector solo se usa internamente en MyClass , pero no es necesario en absoluto para interactuar con esta clase .

Como resultado, pude escribir

Versión 1: SomeOtherHeader.hpp

#pragma once #include "MyClass.hpp" void func(const MyClass&, const std::vector<double>&);

mientras que probablemente debería escribir

Versión 2: SomeOtherHeader.hpp

#pragma once #include "MyClass.hpp" #include <vector> void func(const MyClass&, const std::vector<double>&);

para evitar una dependencia en el funcionamiento interno de MyClass . ¿O debería?

Obviamente, me doy cuenta de que MyClass necesita <vector> para funcionar. Así que esto puede ser más una cuestión filosófica. Pero, ¿no sería bueno poder decidir qué encabezados se exponen al importar (es decir, limitar lo que se carga en el espacio de nombres)? ¿De modo que cada encabezado debe #include lo que necesita, sin alejarse al incluir implícitamente algo que otro encabezado necesitaba en la cadena?

Quizás la gente también pueda arrojar algo de luz sobre los próximos módulos de C ++ 20 que, en mi opinión, abordan algunos aspectos de este problema.


Como han dicho otros, es más seguro incluir directamente los archivos que utiliza, en términos de estar protegido de futuros cambios en el archivo en el que está confiando para reenviarlo.

En general, también se considera más limpio tener sus dependencias inmediatamente allí. Si desea verificar qué es este objeto "MyClass", solo tiene que desplazarse hasta la parte superior y pedirle a su IDE que lo lleve al encabezado correspondiente.

Vale la pena señalar que es seguro incluir el mismo encabezado estándar varias veces, como lo proporciona una garantía estándar de la biblioteca. En la práctica, eso significa que la implementación de ( en el ejemplo, libc ++ de clang ) comenzará con una guarda #include . Los compiladores modernos están tan familiarizados con la inclusión del lenguaje guardián (especialmente cuando se aplican en sus propias implementaciones de biblioteca estándar) que pueden evitar incluso cargar los archivos. Así que lo único que pierde a cambio de esa seguridad y claridad es tener que escribir una docena de letras adicionales.

Todo lo que se acordó con todos los demás, lo he vuelto a leer y no creo que su pregunta fuera en realidad "¿Debo hacer esto?" tanto como "¿Por qué se me permite no hacer esto?" O "¿Por qué el compilador no me aísla de mis inclusiones ''incluye?"

Hay una excepción importante a la regla "incluir directamente lo que usas". Esos son los encabezados que, como parte de su especificación , incluyen encabezados adicionales. Por ejemplo, < iostream > (que, por supuesto, es parte de la biblioteca estándar) está garantizado en c ++ 11 para incluir <istream> y <ostream> . Se podría decir "¿por qué no simplemente el contenido de <istream> y <ostream> movido a <iostream> directamente?" pero hay ventajas de claridad y velocidad de compilación al tener la opción de dividirlas si solo se necesita una. (Y, sin duda para c ++, también hay razones históricas) Por supuesto, también puede hacer esto para sus propios encabezados. (Es más una cosa de Objective-C, pero tienen la misma mecánica de uso y las usan convencionalmente para encabezados de sombrilla, cuyo único trabajo es incluir otros archivos).

Hay otra razón fundamental por la que los encabezados de tu inclusión incluyen la inclusión. Es que, en general, sus encabezados no tienen sentido sin ellos. Supongamos que su archivo MyClass.hpp contiene el siguiente sinónimo de tipo

using NumberPack = std::vector<unsigned int>;

y la siguiente función auto-descriptiva

NumberPack getFirstTenNumers();

Ahora suponga que otro archivo incluye MyClass.hpp y tiene lo siguiente.

NumberPack counter = getFirstTenNumbers(); for (auto c : counter) { std::cout << c << "/n" }

Lo que sucede aquí es que es posible que no desee escribir en su código que está utilizando <vector> . Ese es un detalle de implementación del que no debes tener que preocuparte. NumberPack podría, en lo que a usted concierne, implementarse como algún otro contenedor, un iterador, un generador o una cosa más, siempre que siga su especificación. Pero el compilador necesita saber qué es en realidad: no puede hacer un uso efectivo de las dependencias de los padres sin saber cuáles son los encabezados de dependencia de los abuelos. Un efecto secundario de esto es que te salgas con la suya.

O, por supuesto, la tercera razón es simplemente "porque eso no es C ++". Sí, uno podría tener un lenguaje en el que no se pasaran las dependencias de segunda generación, o tenía que solicitarlo expresamente. Es solo que sería un idioma diferente y, en particular, no encajaría en el estilo de texto antiguo incluido en c ++ o amigos.


Debe usar #include s explícito para tener un flujo de trabajo no destructivo. Digamos que MyClass se utiliza en 50 archivos de origen diferentes. No incluyen vector . De repente, debes cambiar std::vector en MyClass.h para algún otro contenedor. Luego, todos los 50 archivos de origen deberán incluir vector o deberá dejarlo en MyClass.h . Esto sería redundante y podría aumentar el tamaño de la aplicación , el tiempo de compilación e incluso el tiempo de ejecución (inicialización de variable estática) innecesariamente.


Sí, el archivo que usa debe incluir <vector> explícitamente, ya que es una dependencia que necesita.

Sin embargo, no me preocuparía. Si alguien refactoriza MyClass.hpp para eliminar la MyClass.hpp de <vector> , el compilador los dirigirá a todos los archivos que carecían de la inclusión explícita de <vector> , confiando en la inclusión implícita. Por lo general, no es difícil solucionar este tipo de errores, y una vez que el código se compila nuevamente, algunos de los complementos explícitos faltantes se habrán corregido.

Al final, el compilador es mucho más eficiente para detectar perdidas que cualquier otro ser humano.


Si su MyClass tiene un miembro de tipo std::vector<double> entonces el encabezado que define MyClass debe #include <vector> . De lo contrario, la única forma en que los usuarios de MyClass pueden compilar es si #include <vector> antes de incluir la definición de MyClass .

Aunque el miembro es private , sigue siendo parte de la clase, por lo que el compilador necesita ver una definición de tipo completa. De lo contrario, no puede hacer cosas como compute sizeof(MyClass) , o instanciar cualquier objeto MyClass .

Si desea romper la dependencia entre su encabezado y <vector> hay técnicas. Por ejemplo, el lenguaje pimpl ("puntero a implementación").

class MyClass { public: MyClass(double first_value); /* ... */ private: void *pimpl; };

y, en el archivo fuente que define a los miembros de la clase;

#include <vector> #include "MyClass.hpp" MyClass::MyClass(double first_value) : pimpl(new std::vector<double>()) { }

(y también, presumiblemente, hacer algo con first_value , pero lo he omitido).

La compensación es que cada función miembro que necesita usar el vector necesita obtenerla del pimpl . Por ejemplo, si desea obtener una referencia al vector asignado

void MyClass::some_member_function() { std::vector<double> &internal_data = *static_cast<std::vector<double> *>(pimpl); }

El destructor de MyClass también deberá liberar el vector asignado dinámicamente.

Esto también limita algunas opciones para la definición de la clase. Por ejemplo, MyClass no puede tener una función miembro que devuelva un std::vector<double> por valor (a menos que #include <vector> )

Tendrá que decidir si las técnicas como el lenguaje pimpl valen el esfuerzo para hacer que su clase funcione. Personalmente, a menos que existan OTRAS razones convincentes para separar la implementación de la clase de la clase utilizando el lenguaje pimpl, simplemente aceptaría la necesidad de #include <vector> en su archivo de encabezado.


Tenga en cuenta que el código no se escribe solo una vez, sino que evoluciona con el tiempo.

Asumamos que escribiste el código y ahora mi tarea sería refactorizarlo. Por alguna razón, quiero reemplazar MyClass con YourClass y digamos que tienen la misma interfaz. Simplemente tendría que reemplazar cualquier aparición de MyClass con YourClass para llegar a esto:

/* Version 1: SomeOtherHeader.hpp */ #pragma once #include "YourClass.hpp" void func(const YourClass& a, const std::vector<double>& b);

Hice todo correctamente, pero aún así el código no se compilaría (porque YourClass no incluye std::vector ). En este ejemplo en particular, obtendría un mensaje de error claro y la solución sería obvia. Sin embargo, las cosas pueden complicarse bastante rápido si tales dependencias se extienden a lo largo de varios encabezados, si hay muchas de esas dependencias y si SomeOtherHeader.hpp contiene más que una sola declaración.

Hay más cosas que pueden salir mal. Por ejemplo, el autor de MyClass podría decidir que realmente puede eliminar la inclusión a favor de una declaración en adelante. También entonces SomeOtherHeader se romperá. Se reduce a: Si no incluye vector en SomeOtherHeader entonces hay una dependencia oculta, que es mala.

La regla de oro para evitar tales problemas es: Incluya lo que usa.