A seguinte informação está desatualizada. Ele precisa ser atualizado de acordo com o último rascunho do Concepts Lite.
A seção 3 da proposta de restrições cobre isso em uma profundidade razoável.
A proposta de conceitos foi colocada em banho-maria por um curto período na esperança de que as restrições (ou seja, concepts-lite) possam ser concretizadas e implementadas em uma escala de tempo mais curta, atualmente visando pelo menos algo em C ++ 14. A proposta de restrições é projetada para atuar como uma transição suave para uma definição posterior de conceitos. As restrições fazem parte da proposta de conceitos e são um bloco de construção necessário em sua definição.
Em Design of Concept Libraries for C ++ , Sutton e Stroustrup consideram o seguinte relacionamento:
Conceitos = Restrições + Axiomas
Para resumir rapidamente seus significados:
- Restrição - um predicado sobre propriedades avaliadas estaticamente de um tipo. Requisitos puramente sintáticos. Não é uma abstração de domínio.
- Axiomas - Requisitos semânticos de tipos que são considerados verdadeiros. Não verificado estaticamente.
- Conceitos - Requisitos gerais e abstratos de algoritmos em seus argumentos. Definido em termos de restrições e axiomas.
Portanto, se você adicionar axiomas (propriedades semânticas) às restrições (propriedades sintáticas), obterá conceitos.
Concepts-Lite
A proposta de conceitos leves nos traz apenas a primeira parte, restrições, mas este é um passo importante e necessário em direção a conceitos plenamente desenvolvidos.
Restrições
As restrições são todas sobre sintaxe . Eles nos fornecem uma maneira de discernir estaticamente as propriedades de um tipo em tempo de compilação, para que possamos restringir os tipos usados como argumentos de modelo com base em suas propriedades sintáticas. Na proposta atual de restrições, elas são expressas com um subconjunto de cálculo proposicional usando conectivos lógicos como &&
e ||
.
Vamos dar uma olhada em uma restrição em ação:
template <typename Cont>
requires Sortable<Cont>()
void sort(Cont& container);
Aqui estamos definindo um template de função chamado sort
. A nova adição é a cláusula requer . A cláusula require fornece algumas restrições sobre os argumentos do template para esta função. Em particular, essa restrição diz que o tipo Cont
deve ser um Sortable
tipo. O interessante é que pode ser escrito de uma forma mais concisa como:
template <Sortable Cont>
void sort(Cont& container);
Agora, se você tentar passar qualquer coisa que não seja considerada Sortable
para esta função, você obterá um belo erro que imediatamente informa que o tipo deduzido para T
não é um Sortable
tipo. Se você tivesse feito isso em C ++ 11, teria ocorrido um erro horrível lançado de dentro da sort
função que não faria sentido para ninguém.
Os predicados de restrições são muito semelhantes aos traços de tipo. Eles pegam algum tipo de argumento de modelo e fornecem algumas informações sobre ele. As restrições tentam responder aos seguintes tipos de perguntas sobre o tipo:
- Esse tipo tem tal operador sobrecarregado?
- Esses tipos podem ser usados como operandos para este operador?
- Esse tipo tem tal e tal característica?
- Esta expressão constante é igual a isso? (para argumentos de modelo sem tipo)
- Esse tipo tem uma função chamada yada-yada que retorna esse tipo?
- Esse tipo atende a todos os requisitos sintáticos para ser usado dessa forma?
No entanto, as restrições não têm como objetivo substituir as características de tipo. Em vez disso, eles trabalharão de mãos dadas. Alguns traços de tipo agora podem ser definidos em termos de conceitos e alguns conceitos em termos de traços de tipo.
Exemplos
Portanto, o importante sobre as restrições é que elas não se importam nem um pouco com a semântica. Alguns bons exemplos de restrições são:
Equality_comparable<T>
: Verifica se o tipo tem ==
com os dois operandos do mesmo tipo.
Equality_comparable<T,U>
: Verifica se há um ==
com operandos esquerdo e direito dos tipos fornecidos
Arithmetic<T>
: Verifica se o tipo é aritmético.
Floating_point<T>
: Verifica se o tipo é um tipo de ponto flutuante.
Input_iterator<T>
: Verifica se o tipo suporta as operações sintáticas que um iterador de entrada deve suportar.
Same<T,U>
: Verifica se os tipos fornecidos são iguais.
Você pode experimentar tudo isso com uma compilação leve de conceitos especiais do GCC .
Beyond Concepts-Lite
Agora vamos entrar em tudo além da proposta de conceitos leves. Isso é ainda mais futurista do que o próprio futuro. De agora em diante, tudo provavelmente mudará um pouco.
Axiomas
Os axiomas têm tudo a ver com semântica . Eles especificam relacionamentos, invariantes, garantias de complexidade e outras coisas semelhantes. Vejamos um exemplo.
Embora a Equality_comparable<T,U>
restrição diga que existe um operator==
que aceita tipos T
e U
, ela não diz o que essa operação significa . Para isso, teremos o axioma Equivalence_relation
. Este axioma diz que quando objetos desses dois tipos são comparados com operator==
doação true
, esses objetos são equivalentes. Isso pode parecer redundante, mas certamente não é. Você poderia facilmente definir um operator==
que se comportasse como um operator<
. Você seria mau em fazer isso, mas você poderia.
Outro exemplo é um Greater
axioma. É muito bom dizer que dois objetos do tipo T
podem ser comparados com os operadores >
e <
, mas o que eles significam ? O Greater
axioma diz que se f x
é maior então y
, então y
é menor que x
. A especificação proposta, tal axioma, se parece com:
template<typename T>
axiom Greater(T x, T y) {
(x>y) == (y<x);
}
Portanto, os axiomas respondem aos seguintes tipos de perguntas:
- Esses dois operadores têm essa relação um com o outro?
- Este operador para tal e tal tipo significa isso?
- Essa operação nesse tipo tem essa complexidade?
- Este resultado desse operador implica que isso é verdade?
Ou seja, eles se preocupam inteiramente com a semântica de tipos e operações nesses tipos. Essas coisas não podem ser verificadas estaticamente. Se isso precisar ser verificado, um tipo deve de alguma forma proclamar que está de acordo com essa semântica.
Exemplos
Aqui estão alguns exemplos comuns de axiomas:
Equivalence_relation
: Se dois objetos se comparam ==
, eles são equivalentes.
Greater
: Sempre x > y
, então y < x
.
Less_equal
: Sempre x <= y
, então !(y < x)
.
Copy_equality
: Para x
e y
de tipo T
: se x == y
, um novo objeto do mesmo tipo criado por construção de cópia T{x} == y
e ainda x == y
(ou seja, não é destrutivo).
Conceitos
Agora, os conceitos são muito fáceis de definir; eles são simplesmente a combinação de restrições e axiomas . Eles fornecem um requisito abstrato sobre a sintaxe e a semântica de um tipo.
Como exemplo, considere o seguinte Ordered
conceito:
concept Ordered<Regular T> {
requires constraint Less<T>;
requires axiom Strict_total_order<less<T>, T>;
requires axiom Greater<T>;
requires axiom Less_equal<T>;
requires axiom Greater_equal<T>;
}
Em primeiro lugar, observe que para que o tipo de modelo T
seja Ordered
, ele também deve atender aos requisitos do Regular
conceito. O Regular
conceito é um requisito muito básico de que o tipo seja bem comportado - pode ser construído, destruído, copiado e comparado.
Além desses requisitos, o Ordered
requer que T
cumpra uma restrição e quatro axiomas:
- Restrição: um
Ordered
tipo deve ter um operator<
. Isso é verificado estaticamente, portanto, deve existir.
- Axiomas: Para
x
e y
do tipo T
:
x < y
dá uma ordem total estrita.
- Quando
x
é maior que y
, y
é menor que x
e vice-versa.
- Quando
x
é menor ou igual a y
, y
não é menor que x
e vice-versa.
- Quando
x
é maior ou igual a y
, y
não é maior que x
e vice-versa.
Combinar restrições e axiomas como esse fornece conceitos. Eles definem os requisitos sintáticos e semânticos para tipos abstratos para uso com algoritmos. Os algoritmos atualmente têm que assumir que os tipos usados suportarão certas operações e expressarão certas semânticas. Com conceitos, seremos capazes de garantir que os requisitos sejam atendidos.
No projeto de conceitos mais recentes , o compilador só verifica se os requisitos sintáticos de um conceito são atendidos pelo argumento do modelo. Os axiomas não são verificados. Como os axiomas denotam semânticas que não são estaticamente avaliáveis (ou muitas vezes impossíveis de verificar inteiramente), o autor de um tipo teria que declarar explicitamente que seu tipo atende a todos os requisitos de um conceito. Isso era conhecido como mapeamento de conceito em projetos anteriores, mas foi removido desde então.
Exemplos
Aqui estão alguns exemplos de conceitos:
Regular
os tipos são construtíveis, destrutíveis, copiáveis e podem ser comparados.
Ordered
tipos suportam operator<
e têm uma ordem total estrita e outras semânticas de ordem.
Copyable
os tipos podem ser construídos por cópia, destrutíveis e, se x
for igual a y
e x
for copiado, a cópia também será comparada igual a y
.
Iterator
tipos tipos devem ter associadas value_type
, reference
, difference_type
, e iterator_category
que se deve atender a certos conceitos. Eles também devem suportar operator++
e ser desreferenciáveis.
The Road to Concepts
As restrições são o primeiro passo em direção a um recurso de conceitos completos do C ++. Eles são uma etapa muito importante, porque fornecem os requisitos de tipos que podem ser executados estaticamente para que possamos escrever funções e classes de modelo muito mais limpas. Agora podemos evitar algumas das dificuldades e feiúras de std::enable_if
e seus amigos de metaprogramação.
No entanto, há uma série de coisas que a proposta de restrições não faz:
Não fornece uma linguagem de definição de conceito.
As restrições não são mapas conceituais. O usuário não precisa anotar especificamente seus tipos para atender a certas restrições. Eles são verificados estaticamente usando recursos de linguagem de tempo de compilação simples.
As implementações de modelos não são restringidas pelas restrições de seus argumentos de modelo. Ou seja, se o seu template de função faz algo com um objeto de tipo restrito que não deveria fazer, o compilador não tem como diagnosticar isso. Uma proposta de conceitos com todos os recursos seria capaz de fazer isso.
A proposta de restrições foi projetada especificamente para que uma proposta de conceitos completos possa ser introduzida em cima dela. Com alguma sorte, essa transição deve ser um passeio bastante suave. O grupo de conceitos está procurando introduzir restrições para C ++ 14 (ou em um relatório técnico logo depois), enquanto conceitos completos podem começar a surgir em algum momento em torno do C ++ 17.