Dado:
#include <concepts>
#include <iostream>
template<class T>
struct wrapper;
template<std::signed_integral T>
struct wrapper<T>
{
wrapper() = default;
void print()
{
std::cout << "signed_integral" << std::endl;
}
};
template<std::integral T>
struct wrapper<T>
{
wrapper() = default;
void print()
{
std::cout << "integral" << std::endl;
}
};
int main()
{
wrapper<int> w;
w.print(); // Output : signed_integral
return 0;
}
Do código acima, int
qualifica-se para ambos std::integral
e std::signed_integral
conceito.
Surpreendentemente, isso compila e imprime "assinado_integral" nos compiladores GCC e MSVC. Eu esperava que falhasse com um erro nas linhas de "especialização de modelo já definida".
Ok, isso é legal, é justo, mas por que foi std::signed_integral
escolhido em vez de std::integral
? Existe alguma regra definida no padrão com qual especialização de modelo é escolhida quando vários conceitos se qualificam para o argumento do modelo?
c++
language-lawyer
c++20
c++-concepts
Lewis Liman
fonte
fonte
Respostas:
Isso ocorre porque os conceitos podem ser mais especializados que outros, um pouco como os modelos se ordenam. Isso é chamado de ordenação parcial de restrições
No caso de conceitos, eles se substituem quando incluem restrições equivalentes. Por exemplo, veja como
std::integral
estd::signed_integral
são implementados:Normalizando as restrições, o compilador reduz a expressão de contraint para isso:
Neste exemplo,
signed_integral
implicaintegral
completamente. Portanto, de certa forma, uma integral assinada é "mais restrita" do que uma integral.O padrão escreve assim:
De [temp.func.order] / 2 (ênfase minha):
Isso significa que, se houver várias substituições possíveis para um modelo e ambas forem escolhidas na ordem parcial, ele selecionará o modelo mais restrito.
De [temp.constr.order] / 1 :
Isso descreve o algoritmo de subsunção que o compilador usa para ordenar restrições e, portanto, conceitos.
fonte
O C ++ 20 possui um mecanismo para decidir quando uma entidade restrita específica é "mais restrita" que outra. Isso não é uma coisa simples.
Isso começa com o conceito de quebrar uma restrição em seus componentes atômicos, um processo chamado normalização de restrição . É grande e muito complexo para abordar aqui, mas a idéia básica é que cada expressão em uma restrição seja dividida em suas partes conceituais atômicas, recursivamente, até que você atinja uma sub-expressão componente que não é um conceito.
Portanto, vejamos como os conceitos
integral
e são definidos :signed_integral
A decomposição de
integral
é justais_integral_v
. A decomposição designed_integral
éis_integral_v && is_signed_v
.Agora, chegamos ao conceito de subsunção de restrição . É meio complicado, mas a idéia básica é que se diz que uma restrição C1 "subsume" uma restrição C2 se a decomposição de C1 contiver todas as subexpressões em C2. Podemos ver que
integral
não subsumesigned_integral
, massigned_integral
faz subsumirintegral
, uma vez que contém tudo o queintegral
faz.Em seguida, passamos a ordenar entidades restritas:
Como
signed_integral
subsumeintegral
, o<signed_integral> wrapper
é "pelo menos tão restrito" quanto o<integral> wrapper
. No entanto, o inverso não é verdadeiro, porque a subsunção não é reversível.Portanto, de acordo com a regra para entidades "mais restritas":
Como o
<integral> wrapper
não é pelo menos tão restrito quanto<signed_integral> wrapper
, o último é considerado mais restrito que o anterior.E, portanto, quando os dois puderam se candidatar, a declaração mais restrita vence.
Esteja ciente de que as regras de subsunção de restrição param quando uma expressão é encontrada que não é a
concept
. Então, se você fez isso:Nesse caso,
my_signed_integral
não iria desaparecerstd::integral
. Emboramy_is_integral_v
seja definido de forma idênticastd::is_integral_v
, por não ser um conceito, as regras de subsunção do C ++ não podem examiná-lo para determinar se são iguais.Portanto, as regras de subsunção encorajam você a construir conceitos fora das operações em conceitos atômicos.
fonte
Com Partial_ordering_of_constraints
e
E conceito
std::signed_integral
incluistd::integral<T>
conceito:Portanto, seu código está ok, assim como
std::signed_integral
é mais "especializado".fonte