Als ich mir die mögliche Implementierung des same_as-Konzepts unter https://en.cppreference.com/w/cpp/concepts/same_as ansah, bemerkte ich, dass etwas Seltsames passiert.
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>;
Die erste Frage ist, warum ein SameHelper
Konzept nedded ist. Der zweite Grund ist, warum same_as
überprüft wird, ob T
es dasselbe ist wie U
und U
dasselbe wie T
? Ist es nicht überflüssig?
SameHelper<T, U>
es wahr sein könnte, heißt das nicht, dass es wahr seinSameHelper<U, T>
könnte.is_same<T, U>::value == true
wenn und nur wennis_same<U, T>::value == true
." Dies impliziert, dass diese doppelte Überprüfung nicht notwendig istAntworten:
Interessante Frage. Ich habe kürzlich Andrew Suttons Vortrag über Konzepte gesehen und in der Q & A-Sitzung hat jemand die folgende Frage gestellt (Zeitstempel im folgenden Link): CppCon 2018: Andrew Sutton „Konzepte in 60: Alles, was Sie wissen müssen und nichts, was Sie nicht wissen“
Die Frage läuft also darauf hinaus:
If I have a concept that says A && B && C, another says C && B && A, would those be equivalent?
Andrew antwortete mit Ja, wies jedoch darauf hin, dass der Compiler über einige interne Methoden verfügt (die für den Benutzer transparent sind), um die Konzepte in atomare logische Sätze zu zerlegen (atomic constraints
wie Andrew den Begriff formulierte) und zu prüfen, ob dies der Fall ist Äquivalent.Schauen Sie sich nun an, was cppreference über Folgendes sagt
std::same_as
:Es ist im Grunde eine "Wenn-und-Nur-Wenn" -Beziehung: Sie implizieren sich gegenseitig. (Logische Äquivalenz)
Meine Vermutung ist, dass hier die atomaren Zwänge sind
std::is_same_v<T, U>
. Die Art und Weise, wie Compiler damit umgehen,std::is_same_v
könnte sie zum Nachdenken anregenstd::is_same_v<T, U>
undstd::is_same_v<U, T>
als zwei verschiedene Einschränkungen (sie sind verschiedene Einheiten!). Wenn Sie alsostd::same_as
nur eine davon implementieren :Dann
std::same_as<T, U>
undstd::same_as<U, T>
würde zu verschiedenen atomaren Zwängen "explodieren" und nicht gleichwertig werden.Warum kümmert es den Compiler?
Betrachten Sie dieses Beispiel :
Idealerweise
my_same_as<T, U> && std::integral<T>
subsumiertmy_same_as<U, T>
; Daher sollte der Compiler die zweite Vorlagenspezialisierung auswählen, außer ... nicht: Der Compiler gibt einen Fehler auserror: call of overloaded 'foo(int, int)' is ambiguous
.Der Grund dafür ist , dass da
my_same_as<U, T>
undmy_same_as<T, U>
nicht sie nicht subsumieren,my_same_as<T, U> && std::integral<T>
undmy_same_as<U, T>
unvergleichlich werden (auf dem teilweise geordneten Satz von Einschränkungen nach der Beziehung von Subsumtion).Wenn Sie jedoch ersetzen
mit
Der Code wird kompiliert.
quelle
SameHelper
sondern auch : Die beiden Verwendungen vonis_same_v
leiten sich aus demselben Ausdruck ab.is_same<T, U>
identisch istis_same<U, T>
, werden zwei atomare Einschränkungen nur dann als identisch angesehen, wenn sie auch aus demselben Ausdruck gebildet werden. Daher die Notwendigkeit für beide.are_same_as
?template<typename T, typename U0, typename... Un> concept are_same_as = SameAs<T, U0> && (SameAs<T, Un> && ...);
würde in einigen Fällen scheitern. Zum Beispielare_same_as<T, U, int>
wäre äquivalent zu,are_same_as<T, int, U>
aber nicht zuare_same_as<U, T, int>
std::is_same
wird genau dann als wahr definiert, wenn:Soweit ich weiß, definiert Standard nicht die Bedeutung von "gleichem Typ", aber in natürlicher Sprache und Logik ist "gleich" eine Äquivalenzbeziehung und daher kommutativ.
Angesichts dieser Annahme, die ich zuschreibe,
is_same_v<T, U> && is_same_v<U, V>
wäre dies in der Tat überflüssig. Istsame_as
aber nicht spezifiziert in Bezug aufis_same_v
; das ist nur zur Darstellung.Die explizite Prüfung für beide ermöglicht es, dass die Implementierung
same-as-impl
erfüllt wird,same_as
ohne kommutativ zu sein. Wenn Sie es auf diese Weise angeben, wird genau beschrieben, wie sich das Konzept verhält, ohne die Implementierung einzuschränken.is_same_v
Ich weiß nicht genau, warum dieser Ansatz gewählt wurde, anstatt ihn zu spezifizieren . Ein Vorteil des gewählten Ansatzes besteht wohl darin, dass die beiden Definitionen entkoppelt sind. Eins hängt nicht vom anderen ab.quelle