2015-12-22 22 views
15

Szablon klasy std::common_type oblicza wspólny typ dla listy typu variadic. It is defined using the return type of the ternary operator x:y?z recursively. Z tej definicji nie jest dla mnie oczywiste, czy obliczanie std::common_type<X,Y> jest asocjacyjne, tj. mi. czyCzy stowarzyszenie ma postać "std :: typ_typowy"?

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, ""); 

nigdy nie będzie rzucać się błąd kompilacji dla wszystkich typów X, Y i Z dla którego ekspresja is_same<...> jest prawidłowy.

Należy pamiętać, że nie jestem z pytaniem, czy

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

nigdy ogień. Oczywiście nie. Powyższe jest zupełnie inne pytanie.

Należy również zauważyć, że specyfikacja std::common_type zmieniła się nieznacznie w C++ 14 i prawdopodobnie zmieni się ponownie w C++ 17. Odpowiedzi mogą być różne dla różnych wersji standardu.

+2

Czy zaakceptujesz przypadek, w którym '(X, (Y, Z)) ma typ wspólny, ale' (X, Y) ', a zatem' (X, Y), Z', nie? A może chcesz, aby oboje zdecydowali się na typ, ale inni. Ponieważ utknąłem, próbując skonstruować dla niego przypadek i mocno podejrzewam, że nie jest to możliwe; jakiekolwiek dwa łańcuchy konwersji spowodują dwuznaczność. – BoBTFish

+0

Z pewnością przypadek, w którym static_sersertion nie może się skompilować, ponieważ nie ma wspólnego. Zakładam, że pytanie dotyczy pytania, w którym wszystkie typy 'typ_typu' są poprawne, ale' nazwa_systemu' jest fałszywa. –

+0

@ JonathanWakely Dzięki za podpowiedź. Naprawiono pytanie. –

Odpowiedz

11

Nie powiedzie się na MinGW-w64 (gcc 4.9.1). Również nie działa na VS2013 i (dzięki Baum mit Augen) na gcc5.2 lub clang 3.7 z 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

Nie powiedzie się również z gcc5.2 i clang 3.7 z libC++. –

+0

Również kończy się niepowodzeniem na clangu 3.8.0 i gcc 7.0.0 (migawka) –

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"); 
} 

Ouch! Gimnastyka umysłowa szkodzi mi tutaj, ale wymyśliłem przypadek, którego nie udało się skompilować, drukując "Do not match", z gcc 4.9.2 iz "C++ 14" (gcc 5.1) na ideone. Teraz, czy to jest zgodne to inna sprawa ...

teraz roszczenie jest dla typów klasowych std::common_type_t<X, Y> powinny być X lub Y, ale mam zmuszany std::common_type_t<T2, T3> do konwersji na int.

Proszę spróbować z innymi kompilatorami i daj mi znać, co się stanie!

+1

Również uruchamia się z clang3.7 + libC++. –

+0

pożary z mingw-w64 też. VS2013 uruchamia wszystkie z nich (i kilka innych błędów) oprócz "Nie T2". To prawda, że ​​VS nie jest znany ze swojej zgodności ze Standardem ... –

+0

Moja interpretacja normy sugeruje, że drugi przypadek jest źle sformułowany, ponieważ int i T1 mogą nawzajem się nawzajem przekształcać. To sprawia, że ​​nie kwalifikują się one jako operandy 2 i 3 operatora :? –

2

To nie jest skojarzenie! Oto program, w którym to się nie powiedzie:

#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."); 

Pomysł jest prosty: Poniżej przedstawiono schemat, co common_type oblicza:

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

Pierwsza linia jest logiczne, ponieważ X można przekształcić w Y, ale nie odwrotnie. To samo dotyczy pozostałych dwóch linii. Po połączeniu X i Y i ponownym połączeniu z Z otrzymujemy Z. Z drugiej strony, łączenie Y i Z i łączenie z wynikiem daje X. Dlatego wyniki są różne.

Podstawową przyczyną tego jest to, że wymienialność nie jest przechodnia, i. mi. jeśli X jest zamieniany na Y i Y zamienny na Z, nie oznacza to, że X można zamienić na Z. Jeśli zmienność była przejściowa, konwersje działałyby w obie strony, a zatem nie można było jednoznacznie obliczyć wartości i doprowadzić do błędu czasu kompilacji.

To rozumowanie jest niezależne od wersji standardowej. Dotyczy to C++ 11, C++ 14 i nadchodzącego C++ 17.

+0

Tak, ale mam filozoficzne obawy dotyczące trzech kategorii, które można odwzorować na siebie, ale tylko cyklicznie. Powiedziałbym, że to nie jest skojarzenie w języku, ale powinno być, w tym samym sensie, że 'operator ==' powinien być zaimplementowany w taki sposób, że 'T a = b; assert (a == b); ' – alfC