2015-12-22 13 views
15

Die Vorlagenklasse std::common_type berechnet einen allgemeinen Typ für eine Variantentypliste. It is defined using the return type of the ternary operator x:y?z recursively. Aus dieser Definition ist es für mich nicht offensichtlich, ob die Berechnung einer std::common_type<X,Y> ist assoziativ, i. e. obIst `std :: common_type` assoziativ?

using namespace std; 
static_assert(is_same<common_type< X, common_type<Y,Z>::type >::type, 
         common_type< common_type<X,Y>::type, Z >::type>::value, ""); 

wird nie ein Fehler bei der Kompilierung für alle Arten X, Y und Z für die werfen die is_same<...> Ausdruck gültig ist.

Bitte beachten Sie, dass ich, ob

static_assert(is_same<common_type<X,Y>::type, 
         common_type<Y,X>::type>::value, ""); 

wird jemals Feuer NICHT bin gefragt. Es wird offensichtlich nicht. Das Obige ist eine ganz andere Frage.

Bitte beachten Sie auch, dass die Spezifikation std::common_type in C++ 14 leicht geändert wurde und sich wahrscheinlich in C++ 17 wieder ändert. Daher können die Antworten für verschiedene Versionen des Standards unterschiedlich sein.

+2

Akzeptieren Sie einen Fall, in dem '(X, (Y, Z))' 'einen gemeinsamen Typ hat, aber' (X, Y) 'und daher' (X, Y), Z' nicht? Oder möchten Sie, dass beide zu einem Typ aufgelöst werden, aber andere? Denn ich stehe fest dabei, einen Fall für Letzteres zu konstruieren, und achte stark darauf, dass es nicht möglich ist; Zwei beliebige Konvertierungsketten verursachen eine Mehrdeutigkeit. – BoBTFish

+0

Es ist sicherlich der Fall, dass die static_assertion nicht kompilieren kann, da es keinen gemeinsamen Typ gibt. Ich nehme an, dass die Frage nach dem Fall fragt, in dem alle 'common_type' Typen gültig sind, aber' is_same' ist falsch. –

+0

@ JonathanWakely Danke für den Hinweis. Die Frage wurde korrigiert. –

Antwort

11

Dies schlägt bei MinGW-w64 fehl (gcc 4.9.1). Scheitert auch an VS2013 und (dank Baum mit Augen) an gcc5.2 oder clang 3.7 mit libC++.

#include <type_traits> 

using namespace std; 

struct Z; 
struct X{operator Z();}; 
struct Y{operator X();}; 
struct Z{operator Y();}; 

static_assert(is_same<common_type<X,Y>::type, 
         common_type<Y,X>::type>::value, ""); // PASS 

static_assert(is_same<common_type<X,Z>::type, 
         common_type<Z,X>::type>::value, ""); // PASS 

static_assert(is_same<common_type<Y,Z>::type, 
         common_type<Z,Y>::type>::value, ""); // PASS 

static_assert(is_same<common_type< X, common_type<Y,Z>::type >::type, 
         common_type< common_type<X,Y>::type, Z >::type>::value, ""); // FAIL... 
+2

Scheitert auch mit gcc5.2 und clang 3.7 mit libC++. –

+0

Scheitert auch an clang 3.8.0 und gcc 7.0.0 (Snapshot) –

5
#include <type_traits> 

struct T2; 
struct T1 { 
    T1(){} 
    T1(int){} 
    operator T2(); 
}; 
struct T2 { 
    operator int() { return 0; } 
}; 
struct T3 { 
    operator int() { return 0; } 
}; 
T1::operator T2() { return T2(); } 

using namespace std; 
using X = T1; 
using Y = T2; 
using Z = T3; 
int main() 
{ 

    true?T2():T3(); // int 
    static_assert(std::is_same<std::common_type_t<T2, 
                T3>, 
           int>::value, 
        "Not int"); 

    true?T1():(true?T2():T3()); // T1 
    static_assert(std::is_same<std::common_type_t<T1, 
                std::common_type_t<T2, 
                    T3>>, 
           T1>::value, 
        "Not T1"); 

    // ----------------------------------------- 

    true?T1():T2(); // T2 
    static_assert(std::is_same<std::common_type_t<T1, 
                T2>, 
           T2>::value, 
        "Not T2"); 

    true?(true?T1():T2()):T3(); // int 
    static_assert(std::is_same<std::common_type_t<std::common_type_t<T1, 
                    T2>, 
                T3>, 
           int>::value, 
        "Not int"); 

    // ----------------------------------------- 

    static_assert(is_same<common_type_t< X, common_type_t<Y,Z> >, 
          common_type_t< common_type_t<X,Y>, Z > >::value, 
        "Don't match"); 
} 

Autsch! Die mentale Gymnastik hier hat meinem Kopf wehgetan, aber ich habe einen Fall gefunden, der sich nicht kompilieren lässt und "gt nicht" mit gcc 4.9.2 und mit "C++ 14" (gcc 5.1) unter ideone druckt. Nun, ob das nicht konform ist eine andere Sache ...

nun der Anspruch auf Klassentypen ist, std::common_type_t<X, Y> sollte entweder X oder Y sein, aber ich habe std::common_type_t<T2, T3> gezwungen in zu int konvertieren.

Bitte versuchen Sie es mit anderen Compilern und lassen Sie mich wissen, was passiert!

+1

Wird auch mit clang3.7 + libC++ ausgelöst. –

+0

feuert auch mit mingw-w64. VS2013 löst alle (und einige andere Fehler) mit Ausnahme der "Nicht T2" aus. Zugegeben, VS ist nicht bekannt für seine Konformität mit dem Standard ... –

+0

Meine Interpretation des Standards legt nahe, dass der zweite Fall schlecht ausgebildet ist, weil int und T1 sich gegenseitig konvertieren können. Dies macht sie nicht zu Operanden 2 und 3 des Operators:? –

2

Es ist nicht assoziativ! Hier ist ein Programm, in dem es nicht:

#include <type_traits> 

struct Z; 
struct X { X(Z); }; // enables conversion from Z to X 
struct Y { Y(X); }; // enables conversion from X to Y 
struct Z { Z(Y); }; // enables conversion from Y to Z 

using namespace std;  
static_assert(is_same<common_type< X, common_type<Y,Z>::type >::type, 
         common_type< common_type<X,Y>::type, Z >::type>::value, 
       "std::common_type is not associative."); 

Die Idee ist einfach: Das folgende Diagramm zeigt, was common_type berechnet:

X,Y -> Y 
    Y,Z -> Z 
    X,Z -> X 

Die erste Zeile logisch ist, da X kann Y umgewandelt werden, aber nicht umgekehrt. Das Gleiche gilt für die anderen beiden Zeilen. Sobald X und Y kombiniert und mit Z rekombiniert werden, erhalten wir Z. Auf der anderen Seite ergibt das Kombinieren von Y und Z und das Kombinieren von X mit dem Ergebnis X. Daher sind die Ergebnisse unterschiedlich.

Der grundlegende Grund dafür ist, dass Konvertibilität nicht transitiv ist, d. e. wenn X in Y umwandelbar ist und Y in Z umwandelbar ist, folgt nicht, dass X in Z umwandelbar ist. Wenn Konvertierbarkeit transitiv wäre, dann würden Konvertierungen in beide Richtungen funktionieren, und daher könnte die common_type nicht eindeutig berechnet werden und zu einem Kompilierzeitfehler führen.

Diese Argumentation ist unabhängig von der Standardversion. Es gilt für C++ 11, C++ 14 und das kommende C++ 17.

+0

Ja, aber ich habe philosophische Bedenken über drei Kategorien als ineinander abgebildet werden können, aber nur zyklisch. Ich würde sagen, dass das in der Sprache nicht assoziativ ist, aber es sollte in demselben Sinne sein, dass "operator ==" so implementiert werden sollte, dass "T a = b; behaupten (a == b); ' – alfC

Verwandte Themen