2014-12-02 11 views
5

Một lúc trước, một giải pháp để in ra std :: tuple đã được đăng here. Đối với hầu hết các phần tôi nhận được những gì đang xảy ra. Tôi đang gặp khó khăn khi hiểu những gì đang diễn ra trong hàm print_tuple.Anatomy of pretty print tuple

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

Tôi không hiểu điều gì đang xảy ra trong phần nội dung của hàm này. Theo như tôi có thể nói, nó có liên quan đến việc giải nén Is. Tôi nhận được điều kiện đó, Is == 0 đang kiểm tra xem chúng tôi có ở phần tử đầu hay không.

Vậy điều gì đang xảy ra?

+5

Cấu trúc mã (và sau đó ném đi) một mảng 'int []' từ một danh sách khởi tạo, trong đó mỗi phần tử là 0 nhưng in một phần tử của tuple như là một tác dụng phụ (qua toán tử dấu phẩy). Việc sử dụng danh sách initializer chỉ là để đi vào một bối cảnh mở rộng gói sẽ hoạt động. –

+0

Ah! Vì vậy, cấu trúc 'swallow {...}' là một danh sách khởi tạo cho int []. Tôi không hiểu điều đó ngay từ cái nhìn đầu tiên. – sguzman

Trả lời

9

Hãy cùng xem qua một ví dụ với một tuple tùy ý, nói:

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

Như vậy, chuỗi số nguyên mà chức năng của chúng tôi sẽ được gọi với là:

seq<0, 1, 2> 

Và mở rộng gói của chúng tôi trong nội dung là:

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

, nếu chúng tôi mở rộng theo cách thủ công trình biên dịch sẽ trở thành:

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

Và sau đó đánh giá các ngành:

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

Đó là để nói, chúng tôi đang xây dựng một mảng số nguyên của 4 0 s, với tác dụng phụ của việc in ra các nội dung của tuple, được phân cách bằng dấu phẩy, đảm bảo chúng tôi không bắt đầu bằng dấu phẩy thêm. Bốn biểu thức phải được đánh giá theo thứ tự, để đảm bảo rằng nội dung của bộ tuple được in theo thứ tự.

Đoạn (void) ban đầu chỉ ở đó để tránh cảnh báo không được sử dụng biến mà trình biên dịch sẽ phát ra nếu bạn đã bật tất cả cảnh báo. Ban đầu 0 trong việc khởi tạo mảng xử lý trường hợp tuple rỗng.

+0

Tôi bắt đầu nhận được nó. 'Void (...)' làm gì? – sguzman

+1

@SalvadorGuzman Theo kết quả của 'os << bất kỳ' tới' void', để biểu thức '(void (stuff), 0)' chắc chắn trả về '0'. Điều này được thực hiện trên cơ hội tắt mà ai đó quyết định viết một tình trạng quá tải cho 'operator,()' mà làm một cái gì đó khác hơn là trả về 'int'. – Barry

+0

Điều này có ý nghĩa hơn nhiều. Một câu hỏi cuối cùng. Vậy, toán tử '...' có phải là trình giải nén không? Tôi có thể dựa vào nó để trả về các giá trị được tách nhau bằng dấu phẩy không? – sguzman

1

(os << (Is == 0? "" : ", ") << std::get<Is>(t)) in các yếu tố Is thứ (với tiền tố ", " cho Is > 0)

Sau đó, đúc kết quả để void để tránh khả năng quá tải của nhà điều hành dấu phẩy.

(/*previous stuff*/, 0)... hiện một loạt các 0

{0, /*previous stuff*/ } quản lý các trường hợp sizeof...(Is) == 0

swallow /*previous stuff*/ xây dựng một mảng int của 0.

void /*previous stuff*/: bỏ trống để tránh cảnh báo.