2014-12-02 24 views
5

Jakiś czas temu, rozwiązanie do drukowania std :: tuple zostało opublikowane here. W większości dostaję to, co się dzieje. Mam problem ze zrozumieniem, co dzieje się w funkcji print_tuple.Anatomia ładnej krotki wydruk

template<class Ch, class Tr, class Tuple, std::size_t... Is> 
void print_tuple(std::basic_ostream<Ch,Tr>& os, Tuple const& t, seq<Is...>){ 
    using swallow = int[]; 
    (void)swallow{0, (void(os << (Is == 0? "" : ", ") << std::get<Is>(t)), 0)...}; 
} 

Nie dostaję tego, co dzieje się w ciele tej funkcji. O ile wiem, ma to coś wspólnego z rozpakowywaniem Is. Rozumiem, że warunek, Is == 0 sprawdza, czy jesteśmy w głównym elemencie.

Co się dzieje?

+5

Kod konstruuje (a następnie wyrzuca) tablicę 'int []' z listy inicjalizacyjnej, gdzie każdy element ma wartość 0, ale drukuje jeden element krotki jako efekt uboczny (za pośrednictwem operatora przecinkowego). Używanie listy inicjalizującej jest po prostu przejściem do kontekstu, w którym działałoby rozwinięcie paczki. –

+0

Ah! Zatem konstrukt 'swallow {...}' jest listą inicjalizacji dla int []. Nie zauważyłem tego na pierwszy rzut oka. – sguzman

Odpowiedz

9

Chodźmy na przykładzie z dowolnej krotki, powiedzmy:

using Tuple = tuple<char, int, string>; 

Zatem sekwencja całkowitą że nasza funkcja zostanie wywołana jest:

seq<0, 1, 2> 

A nasz Dodatek w Ciało to:

(void)swallow{0, (void(os << (Is == 0? "" : ", ") << std::get<Is>(t)), 0)...}; 

, które, jeśli ręcznie rozszerzymy go w taki sposób, jak kompilator, stanie się:

(void)swallow{0, 
       (void(os << (0 == 0? "" : ", ") << std::get<0>(t)), 0), 
       (void(os << (1 == 0? "" : ", ") << std::get<1>(t)), 0), 
       (void(os << (2 == 0? "" : ", ") << std::get<2>(t)), 0) 
       }; 

A potem oceniające oddziały:

(void)swallow{0, 
       (void(os << "" << std::get<0>(t)), 0), 
       (void(os << ", " << std::get<1>(t)), 0), 
       (void(os << ", " << std::get<2>(t)), 0) 
       }; 

To znaczy, my konstruowaniu tablicę całkowitą 4 0 s, z efektem ubocznym drukowanie zawartości krotki, oddzielone przecinkami, upewniając się, że nie zaczynamy od dodatkowego przecinka. Cztery wyrażenia muszą być ocenione w kolejności, aby zagwarantować, że zawartość krotki zostanie wydrukowana w kolejności.

Początkowa obsada (void) jest właśnie tutaj, aby uniknąć nieużywanego zmiennego ostrzeżenia, które kompilatory powinny emitować, jeśli wszystkie ostrzeżenia są włączone. Początkowe 0 w inicjalizacji macierzy obsługuje przypadek, w którym krotka jest pusta.

+0

Zaczynam to rozumieć. Co robi 'void (...)'? – sguzman

+1

@SalvadorGuzman Rzuca wynik 'os << cokolwiek' na' void', dzięki czemu wyrażenie '(void (stuff), 0)' zdecydowanie zwraca '0'. Odbywa się to przy braku szansy, że ktoś zdecydował się napisać przeciążenie dla 'operator,()', które robi coś innego niż zwracanie 'int'. – Barry

+0

To ma dużo więcej sensu. Ostatnie pytanie. Czy zatem operator "..." jest rozpakownikiem? Czy mogę liczyć na to, że zwróci wartości rozdzielone przecinkami? – sguzman

1

(os << (Is == 0? "" : ", ") << std::get<Is>(t)) Drukuj ty element Is (z prefiksem ", " do Is > 0)

Następnie odlewania wynik void aby uniknąć ewentualnego przeciążenia operatora przecinkami.

(/*previous stuff*/, 0)... robi serię 0

{0, /*previous stuff*/ } zarządza przypadki sizeof...(Is) == 0

swallow /*previous stuff*/ zbudować int tablica 0.

void /*previous stuff*/: rzucaj do pustki, aby uniknąć ostrzeżenia.