Resposta curta: você está misturando conceitos antes do tempo de compilação e tempo de compilação que têm semelhanças em seus propósitos. As interfaces (classes abstratas e toda a implementação do paradigma de orientação a objetos) são forçadas no momento da compilação . Os conceitos são a mesma idéia, mas no contexto da programação genérica que ocorre em C ++ ANTES do tempo de compilação . Ainda não temos esse último recurso.
Mas deixe-me explicar desde o início.
Resposta longa:
De fato, os Conceitos são apenas aplicação de linguagem e "facilitado para o programador" de algo já presente na linguagem, que você poderia chamar de "digitação de pato".
Quando você passa um tipo para uma função de modelo, que é uma função genérica a partir da qual o compilador gera código real (em linha) quando chamado, esse tipo precisa ter algumas propriedades (características?) Que serão usadas no código do modelo. Portanto, é a idéia de digitar patos, mas tudo é gerado e feito em tempo de compilação .
O que acontece quando o tipo não possui as propriedades necessárias?
Bem, o compilador saberá que há um problema apenas quando o código gerado do modelo for compilado e falhar. Isso significa que o erro que será gerado será um erro dentro do código do modelo, que será mostrado ao programador como seu erro. Além disso, o erro terá toneladas de informações por causa das meta-informações fornecidas no caso de geração de código do modelo, para saber de qual instanciação do modelo estamos falando.
Vários problemas com isso: primeiro, na maioria das vezes, o código de modelo é código de biblioteca e a maioria dos programadores são usuários de código de biblioteca, não criadores de código de biblioteca. Isso significa que esse tipo de erro enigmático é realmente difícil de entender quando você não entende como a biblioteca é escrita (não apenas o design, como é realmente implementada). O segundo problema é que, mesmo quando o programador escreveu o código do modelo, os motivos da falha ainda podem ser obscuros porque o compilador poderá dizer que há um problema tarde demais: quando o código gerado está sendo compilado. Se o problema for relativo às propriedades do tipo , verifique-o antes mesmo de gerar o código.
É para isso que os Conceitos permitem (e são projetados): permitir que o programador (código genérico) especifique as propriedades dos tipos que são passados como parâmetros do modelo e, em seguida, permita que o compilador forneça erros explícitos, caso os tipos fornecidos não atendam às requisitos.
Quando a verificação for bem-sucedida, o código será gerado a partir do modelo e então compilado, certamente com sucesso.
Toda a verificação do conceito ocorre exclusivamente antes do tempo de compilação . Ele verifica os próprios tipos, não os tipos de objeto . Não há objeto antes do tempo de compilação.
Agora, sobre "interfaces".
Ao criar um tipo base abstrato ou virtual, você permite que o código o utilize para manipular objetos dos tipos filhos sem conhecer suas implementações reais. Para impor isso, o tipo base expõe membros que são virtuais e podem ser (ou precisam ser) sobrecarregados pelos tipos filhos.
Isso significa que o compilador pode verificar em tempo de compilação que todos os objetos passados para uma função que requer uma referência à classe base devem ser 1. de um dos tipos filhos da classe base, 2. esse tipo filho deve ter implementações de funções puras virtuais declaradas nas classes base, se houver.
Portanto, no tempo de compilação , o compilador verifica as interfaces dos tipos de objeto e informa se algo está faltando.
É a mesma ideia que Concepts, mas ocorre tarde demais , como foi dito na descrição do Concept. Ocorre no tempo de compilação. Não estamos no código genérico (código do modelo), estamos depois que ele foi processado e já é tarde demais para verificar se os tipos atendem aos requisitos genéricos, que não podem ser expostos pelas classes base virtuais. De fato, toda a implementação do paradigma de orientação a objetos em C ++ nem existe quando o código do modelo está sendo processado. Ainda não há objetos. Isso é
As classes descrevem restrições nos objetos a serem usados para verificar os requisitos de funções que manipulam esses objetos. Os conceitos descrevem restrições nos tipos (incluindo classes) a serem usadas para verificar os requisitos de código genérico para gerar código real a partir desses tipos e combinação de código genérico.
Então, novamente, é o mesmo "teste de sanidade", mas em outra camada da linguagem, ou seja, modelos. Os modelos são uma linguagem completa (turing complete) que permite metaprogramação e tipos de programação mesmo antes de aparecerem no código compilado. É um pouco como criar scripts para o compilador. Digamos que você possa criar um script, classes são apenas valores manipulados pelo script. Atualmente, não há como verificar restrições nesses valores além de travar o script de uma maneira não óbvia. Os conceitos são apenas isso: forneça a digitação nesses valores (que no código gerado são tipos). Não tenho certeza se estou claro ...
Outra diferença realmente importante entre as classes base virtuais e os Conceitos é que a primeira força uma forte relação entre os tipos, tornando-os "ligados pelo sangue". Enquanto a metaprogramação de modelos permite "digitação de pato", os Conceitos apenas permitem tornar os requisitos mais claros.
A resposta simples para praticamente todas as suas perguntas é "Porque os compiladores C ++ são ruins". A sério. Eles são construídos com a tecnologia da unidade de tradução da C, que proíbe efetivamente muitas coisas úteis, e as implementações de modelos existentes são terrivelmente lentas. Os conceitos não foram cortados por qualquer motivo conceitual - eles foram cortados porque não havia uma implementação confiável, o ConceptGCC era extremamente lento e a especificação de conceitos levou um tempo absurdamente longo. Herb Sutter afirmou que era necessário mais espaço para especificar os conceitos usados na biblioteca Padrão do que para especificar o conjunto de modelos.
Indiscutivelmente, entre SFINAE
decltype
estatic_assert
, eles são na maioria implementáveis, como é agora.fonte
No meu entendimento, Interfaces e Conceitos têm propósitos semelhantes em diferentes partes da linguagem C ++.
Conforme mencionado na resposta à pergunta original: A implementação de uma interface é decidida pelo implementador de uma classe no tempo de design. Depois que uma classe é publicada, ela pode suportar apenas as interfaces das quais foi derivada no momento do design.
Duas interfaces distintas com exatamente as mesmas funções-membro e semântica (ou seja, o mesmo conceito) ainda serão duas interfaces distintas. Se você deseja dar suporte à semântica de ambas as interfaces, pode ser necessário implementar o suporte duas vezes.
Esse é o problema que a Programação Genérica pretende solucionar. Na Programação Genérica em C ++, o tipo passado para um modelo simplesmente precisa oferecer suporte à interface (sem maiúsculas, no sentido da "interface de programação" de um tipo) usada pelo modelo. As duas interfaces distintas com as mesmas funções de membro corresponderão e você precisará escrever o código apenas uma vez. Além disso, quaisquer tipos (mesmo sem interfaces explícitas) compatíveis com a mesma interface funcionarão.
Isso leva a uma segunda questão: e se você tiver dois tipos com interfaces sobrepostas, mas semânticas diferentes ? A programação genérica não será capaz de dizer a diferença e tudo será compilado muito bem, mas o resultado do tempo de execução será surpreendente e provavelmente errado.
É aí que entram os conceitos. Se você simplificar demais, pode considerar um conceito como a versão genérica (modelo) de uma interface. Ele precisa ser implementado apenas uma vez para aplicar a um grande número de tipos potenciais que podem "derivar" do Conceito. Um conceito é uma interface semântica predeterminada de um tipo (classe) que não se limita apenas a esse tipo. É diferente de uma Interface, pois dois tipos muito diferentes (para o compilador genérico) ainda podem ter o mesmo Conceito, sem ter que recorrer à limitação de Interfaces da classe base, ou Interfaces da classe base que não possuem semântica real visível do compilador. próprias, mas são usadas apenas para distinção de tipos.
fonte