A resposta tem duas partes. Compatibilidade no nível do compilador e compatibilidade no nível do vinculador. Vamos começar com o primeiro.
vamos supor que todos os cabeçalhos foram escritos em C ++ 11
Usar o mesmo compilador significa que o mesmo cabeçalho da biblioteca padrão e arquivos de origem (os primeiros associados ao compilador) serão usados independentemente do padrão C ++ de destino. Portanto, os arquivos de cabeçalho da biblioteca padrão são escritos para serem compatíveis com todas as versões C ++ suportadas pelo compilador.
Dito isso, se as opções do compilador usadas para compilar uma unidade de tradução especificam um determinado padrão C ++, quaisquer recursos que estão disponíveis apenas em padrões mais novos não devem estar acessíveis. Isso é feito usando a __cplusplus
diretiva. Veja o arquivo de origem do vetor para um exemplo interessante de como ele é usado. Da mesma forma, o compilador rejeitará quaisquer recursos sintáticos oferecidos pelas versões mais recentes do padrão.
Tudo isso significa que sua suposição só pode ser aplicada aos arquivos de cabeçalho que você escreveu. Esses arquivos de cabeçalho podem causar incompatibilidades quando incluídos em diferentes unidades de tradução direcionadas a diferentes padrões C ++. Isso é discutido no Anexo C do padrão C ++. Existem 4 cláusulas, discutirei apenas a primeira e mencionarei brevemente o resto.
C.3.1 Cláusula 2: convenções lexicais
As aspas simples delimitam um literal de caractere em C ++ 11, enquanto são separadores de dígitos em C ++ 14 e C ++ 17. Suponha que você tenha a seguinte definição de macro em um dos arquivos de cabeçalho C ++ 11 puros:
#define M(x, ...) __VA_ARGS__
// Maybe defined as a field in a template or a type.
int x[2] = { M(1'2,3'4) };
Considere duas unidades de tradução que incluem o arquivo de cabeçalho, mas visam C ++ 11 e C ++ 14, respectivamente. Ao direcionar o C ++ 11, a vírgula entre aspas não é considerada um separador de parâmetro; existe apenas um parâmetro. Portanto, o código seria equivalente a:
int x[2] = { 0 }; // C++11
Por outro lado, ao direcionar o C ++ 14, as aspas simples são interpretadas como separadores de dígitos. Portanto, o código seria equivalente a:
int x[2] = { 34, 0 }; // C++14 and C++17
O ponto aqui é que usar aspas simples em um dos arquivos de cabeçalho C ++ 11 puros pode resultar em erros surpreendentes nas unidades de tradução voltadas para C ++ 14/17. Portanto, mesmo se um arquivo de cabeçalho for escrito em C ++ 11, ele deve ser escrito com cuidado para garantir que seja compatível com as versões posteriores do padrão. A __cplusplus
diretiva pode ser útil aqui.
As outras três cláusulas da norma incluem:
C.3.2 Cláusula 3: conceitos básicos
Alteração : Novo desalocador usual (não posicionamento)
Justificativa : necessária para desalocação de tamanho.
Efeito no recurso original : o código C ++ 2011 válido pode declarar uma função de alocação de posicionamento global e função de desalocação da seguinte forma:
void operator new(std::size_t, std::size_t);
void operator delete(void*, std::size_t) noexcept;
Nesta Norma Internacional, entretanto, a declaração de exclusão de operador pode corresponder a uma exclusão de operador usual (não posicionamento) predefinida (3.7.4). Se for assim, o programa está mal formado, como era para funções de alocação de membros de classe e funções de desalocação (5.3.4).
C.3.3 Cláusula 7: declarações
Alteração : as funções de membro não estáticas constexpr não são funções de membro const implicitamente.
Justificativa : Necessário para permitir que funções-membro constexpr alterem o objeto.
Efeito no recurso original : O código C ++ 2011 válido pode falhar ao compilar nesta Norma.
Por exemplo, o código a seguir é válido em C ++ 2011, mas inválido neste Padrão Internacional porque declara a mesma função de membro duas vezes com tipos de retorno diferentes:
struct S {
constexpr const int &f();
int &f();
};
C.3.4 Cláusula 27: biblioteca de entrada / saída
Alteração : get não está definido.
Justificativa : O uso de get é considerado perigoso.
Efeito no recurso original : o código C ++ 2011 válido que usa a função gets pode falhar ao compilar neste Padrão Internacional.
As incompatibilidades potenciais entre C ++ 14 e C ++ 17 são discutidas em C.4. Como todos os arquivos de cabeçalho não padrão são escritos em C ++ 11 (conforme especificado na pergunta), esses problemas não ocorrerão, portanto, não os mencionarei aqui.
Agora vou discutir a compatibilidade no nível do vinculador. Em geral, os motivos potenciais para incompatibilidades incluem o seguinte:
- O formato dos arquivos de objeto.
- Rotinas de inicialização e encerramento do programa e o
main
ponto de entrada.
- Otimização de todo o programa (WPO).
Se o formato do arquivo de objeto resultante depender do padrão C ++ de destino, o vinculador deve ser capaz de vincular os diferentes arquivos de objeto. No GCC, LLVM e VC ++, felizmente esse não é o caso. Ou seja, o formato dos arquivos de objetos é o mesmo, independentemente do padrão de destino, embora seja altamente dependente do próprio compilador. Na verdade, nenhum dos vinculadores de GCC, LLVM e VC ++ requer conhecimento sobre o padrão C ++ de destino. Isso também significa que podemos vincular arquivos-objeto que já estão compilados (vinculando estaticamente o tempo de execução).
Se a rotina de inicialização do programa (a função que chama main
) for diferente para diferentes padrões C ++ e as diferentes rotinas não forem compatíveis entre si, então não seria possível vincular os arquivos-objeto. No GCC, LLVM e VC ++, felizmente esse não é o caso. Além disso, a assinatura da main
função (e as restrições que se aplicam a ela, consulte a Seção 3.6 do padrão) é a mesma em todos os padrões C ++, portanto, não importa em qual unidade de tradução ela existe.
Em geral, o WPO pode não funcionar bem com arquivos-objeto compilados usando padrões C ++ diferentes. Isso depende exatamente de quais estágios do compilador exigem conhecimento do padrão de destino e quais não, e do impacto que isso tem nas otimizações entre procedimentos que cruzam arquivos de objeto. Felizmente, GCC, LLVM e VC ++ são bem projetados e não têm esse problema (não que eu saiba).
Portanto, GCC, LLVM e VC ++ foram projetados para permitir compatibilidade binária em diferentes versões do padrão C ++. Este não é realmente um requisito do padrão em si.
A propósito, embora o compilador VC ++ ofereça a opção std , que permite direcionar uma versão específica do padrão C ++, ele não oferece suporte ao direcionamento C ++ 11. A versão mínima que pode ser especificada é C ++ 14, que é o padrão a partir da atualização 3 do Visual C ++ 2013 para compilar diferentes unidades de tradução que visam diferentes versões do padrão C ++, o que no mínimo quebraria o WPO.
CAVEAT: Minha resposta pode não ser completa ou muito precisa.