En regardant l'implémentation possible du concept same_as sur https://en.cppreference.com/w/cpp/concepts/same_as, j'ai remarqué qu'il se passe quelque chose d'étrange.
namespace detail {
template< class T, class U >
concept SameHelper = std::is_same_v<T, U>;
}
template< class T, class U >
concept same_as = detail::SameHelper<T, U> && detail::SameHelper<U, T>;
La première question est pourquoi un SameHelper
concept est intégré? La seconde est pourquoi same_as
vérifie si T
est le même que U
et U
le même que T
? N'est-ce pas redondant?
SameHelper<T, U>
n'est pas parce que cela pourrait être vrai que celaSameHelper<U, T>
pourrait l'être.is_same<T, U>::value == true
si et seulement siis_same<U, T>::value == true
." Cela implique que cette double vérification n'est pas nécessaireRéponses:
Question interessante. J'ai récemment regardé la conférence d'Andrew Sutton sur les concepts, et lors de la session de questions / réponses, quelqu'un a posé la question suivante (horodatage dans le lien suivant): CppCon 2018: Andrew Sutton «Concepts in 60: Tout ce que vous devez savoir et rien que vous ne savez pas»
Donc, la question se résume à:
If I have a concept that says A && B && C, another says C && B && A, would those be equivalent?
Andrew a répondu oui, mais a souligné le fait que le compilateur a des méthodes internes (qui sont transparentes pour l'utilisateur) pour décomposer les concepts en propositions logiques atomiques (atomic constraints
comme Andrew a formulé le terme) et vérifier si elles sont équivalent.Maintenant, regardez ce que cppreference dit à propos de
std::same_as
:Il s'agit essentiellement d'une relation «si et seulement si»: elles s'impliquent mutuellement. (Équivalence logique)
Ma conjecture est qu'ici les contraintes atomiques sont
std::is_same_v<T, U>
. La façon dont les compilateurs traitentstd::is_same_v
peut les faire réfléchirstd::is_same_v<T, U>
etstd::is_same_v<U, T>
comme deux contraintes différentes (ce sont des entités différentes!). Donc, si vous implémentezstd::same_as
un seul d'entre eux:Alors
std::same_as<T, U>
etstd::same_as<U, T>
"exploserait" à différentes contraintes atomiques et deviendrait pas équivalent.Eh bien, pourquoi le compilateur s'en soucie-t-il?
Considérez cet exemple :
Idéalement,
my_same_as<T, U> && std::integral<T>
subsumemy_same_as<U, T>
; par conséquent, le compilateur doit sélectionner la deuxième spécialisation de modèle, sauf ... ce n'est pas le cas: le compilateur émet une erreurerror: call of overloaded 'foo(int, int)' is ambiguous
.La raison en est que depuis
my_same_as<U, T>
etmy_same_as<T, U>
ne se subsument pas,my_same_as<T, U> && std::integral<T>
etmy_same_as<U, T>
deviennent incomparables (sur l'ensemble de contraintes partiellement ordonnées sous la relation de subsomption).Cependant, si vous remplacez
avec
Le code se compile.
la source
SameHelper
: cela fait que les deux utilisations deis_same_v
dérivent de la même expression.is_same<T, U>
est identique àis_same<U, T>
, deux contraintes atomiques ne sont considérées comme identiques que si elles sont également formées à partir de la même expression. D'où la nécessité des deux.are_same_as
?template<typename T, typename U0, typename... Un> concept are_same_as = SameAs<T, U0> && (SameAs<T, Un> && ...);
échouerait dans certains cas. Par exempleare_same_as<T, U, int>
serait équivalentare_same_as<T, int, U>
mais pas àare_same_as<U, T, int>
std::is_same
est défini comme vrai si et seulement si:Pour autant que je sache, la norme ne définit pas le sens de «même type», mais en langage naturel et en logique, «même» est une relation d'équivalence et est donc commutatif.
Étant donné cette hypothèse, à laquelle je souscris,
is_same_v<T, U> && is_same_v<U, V>
serait en effet redondante. Maissame_as
n'est pas spécifié en termes deis_same_v
; ce n'est que pour l'exposition.La vérification explicite des deux permet à l'implémentation
same-as-impl
de satisfairesame_as
sans être commutative. Le spécifier de cette façon décrit exactement comment le concept se comporte sans restreindre comment il pourrait être mis en œuvre.Exactement pourquoi cette approche a été choisie au lieu de préciser en termes de
is_same_v
, je ne sais pas. Un avantage de l'approche choisie est sans doute que les deux définitions sont découplées. L'un ne dépend pas de l'autre.la source