Hay dos partes en la respuesta. Compatibilidad a nivel de compilador y compatibilidad a nivel de enlazador. Empecemos por el primero.
supongamos que todos los encabezados se escribieron en C ++ 11
Usar el mismo compilador significa que se usarán el mismo encabezado de biblioteca estándar y los mismos archivos fuente (las partes asociadas con el compilador) independientemente del estándar C ++ de destino. Por lo tanto, los archivos de encabezado de la biblioteca estándar se escriben para que sean compatibles con todas las versiones de C ++ admitidas por el compilador.
Dicho esto, si las opciones del compilador utilizadas para compilar una unidad de traducción especifican un estándar de C ++ en particular, entonces las funciones que solo están disponibles en los estándares más nuevos no deberían ser accesibles. Esto se hace usando la __cplusplus
directiva. Consulte el archivo de origen vectorial para ver un ejemplo interesante de cómo se usa. Del mismo modo, el compilador rechazará cualquier característica sintáctica ofrecida por las versiones más nuevas del estándar.
Todo eso significa que su suposición solo se puede aplicar a los archivos de encabezado que escribió. Estos archivos de encabezado pueden causar incompatibilidades cuando se incluyen en diferentes unidades de traducción dirigidas a diferentes estándares C ++. Esto se discute en el Anexo C del estándar C ++. Hay 4 cláusulas, solo discutiré la primera y mencionaré brevemente el resto.
C.3.1 Cláusula 2: convenciones léxicas
Las comillas simples delimitan un carácter literal en C ++ 11, mientras que son separadores de dígitos en C ++ 14 y C ++ 17. Suponga que tiene la siguiente definición de macro en uno de los archivos de encabezado C ++ 11 puros:
#define M(x, ...) __VA_ARGS__
int x[2] = { M(1'2,3'4) };
Considere dos unidades de traducción que incluyen el archivo de encabezado, pero apuntan a C ++ 11 y C ++ 14, respectivamente. Al apuntar a C ++ 11, la coma entre las comillas no se considera un separador de parámetros; solo hay un parámetro. Por tanto, el código sería equivalente a:
int x[2] = { 0 };
Por otro lado, al apuntar a C ++ 14, las comillas simples se interpretan como separadores de dígitos. Por tanto, el código sería equivalente a:
int x[2] = { 34, 0 };
El punto aquí es que el uso de comillas simples en uno de los archivos de encabezado de C ++ 11 puros puede resultar en errores sorprendentes en las unidades de traducción que apuntan a C ++ 14/17. Por lo tanto, incluso si un archivo de encabezado está escrito en C ++ 11, debe escribirse con cuidado para garantizar que sea compatible con versiones posteriores del estándar. La __cplusplus
directiva puede resultar útil aquí.
Las otras tres cláusulas del estándar incluyen:
C.3.2 Cláusula 3: conceptos básicos
Cambio : nuevo desasignador habitual (sin ubicación)
Justificación : Requerido para desasignación de tamaño.
Efecto en la característica original : el código válido de C ++ 2011 podría declarar una función de asignación de ubicación global y una función de desasignación de la siguiente manera:
void operator new(std::size_t, std::size_t);
void operator delete(void*, std::size_t) noexcept;
En esta Norma Internacional, sin embargo, la declaración de eliminación de operador puede coincidir con una eliminación de operador habitual predefinida (sin ubicación) (3.7.4). Si es así, el programa está mal formado, como lo fue para las funciones de asignación de miembros de clase y las funciones de desasignación (5.3.4).
C.3.3 Cláusula 7: declaraciones
Cambio : las funciones miembro no estáticas constexpr no son funciones miembro const implícitamente.
Justificación : Necesario para permitir que las funciones del miembro constexpr muten el objeto.
Efecto sobre la característica original : Es posible que el código C ++ 2011 válido no se compile en esta Norma Internacional.
Por ejemplo, el siguiente código es válido en C ++ 2011 pero no válido en esta Norma Internacional porque declara la misma función miembro dos veces con diferentes tipos de retorno:
struct S {
constexpr const int &f();
int &f();
};
C.3.4 Cláusula 27: biblioteca de entrada / salida
Cambio : obtiene no está definido.
Justificación : El uso de gets se considera peligroso.
Efecto sobre la característica original : el código válido de C ++ 2011 que usa la función gets puede fallar al compilar en esta Norma Internacional.
Las posibles incompatibilidades entre C ++ 14 y C ++ 17 se analizan en C.4. Dado que todos los archivos de encabezado no estándar están escritos en C ++ 11 (como se especifica en la pregunta), estos problemas no ocurrirán, por lo que no los mencionaré aquí.
Ahora discutiré la compatibilidad en el nivel del enlazador. En general, las posibles razones de las incompatibilidades incluyen las siguientes:
Si el formato del archivo de objeto resultante depende del estándar C ++ de destino, el vinculador debe poder vincular los diferentes archivos de objeto. En GCC, LLVM y VC ++, afortunadamente este no es el caso. Es decir, el formato de los archivos de objetos es el mismo independientemente del estándar de destino, aunque depende en gran medida del propio compilador. De hecho, ninguno de los enlazadores de GCC, LLVM y VC ++ requiere conocimientos sobre el estándar C ++ de destino. Esto también significa que podemos vincular archivos de objeto que ya están compilados (vinculando estáticamente el tiempo de ejecución).
Si la rutina de inicio del programa (la función que llama main
) es diferente para diferentes estándares C ++ y las diferentes rutinas no son compatibles entre sí, entonces no sería posible vincular los archivos de objeto. En GCC, LLVM y VC ++, afortunadamente este no es el caso. Además, la firma de la main
función (y las restricciones que se le aplican, consulte la Sección 3.6 del estándar) es la misma en todos los estándares C ++, por lo que no importa en qué unidad de traducción exista.
En general, es posible que WPO no funcione bien con archivos de objeto compilados con diferentes estándares de C ++. Esto depende exactamente de qué etapas del compilador requieren conocimiento del estándar de destino y qué etapas no y el impacto que tiene en las optimizaciones entre procedimientos que cruzan archivos de objetos. Afortunadamente, GCC, LLVM y VC ++ están bien diseñados y no tienen este problema (no que yo sepa).
Por lo tanto, GCC, LLVM y VC ++ se han diseñado para permitir la compatibilidad binaria en diferentes versiones del estándar C ++. Sin embargo, esto no es realmente un requisito de la norma en sí.
Por cierto, aunque el compilador de VC ++ ofrece el modificador std , que le permite apuntar a una versión particular del estándar C ++, no es compatible con C ++ 11. La versión mínima que se puede especificar es C ++ 14, que es la predeterminada a partir de Visual C ++ 2013 Update 3. Podría usar una versión anterior de VC ++ para apuntar a C ++ 11, pero luego tendría que usar diferentes compiladores de VC ++ para compilar diferentes unidades de traducción que tengan como objetivo diferentes versiones del estándar C ++, lo que al menos rompería WPO.
AVISO: Mi respuesta puede no ser completa o muy precisa.