What does this template code expand to?

I found this on Stack Overflow, here, and simplified it slightly:
http://stackoverflow.com/a/6245777

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# include <iostream>
# include <tuple>

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

template<class... Elems>
std::ostream &operator <<(std::ostream& os, std::tuple<Elems...> const &tuple) {
    os << "(";
    print_tuple(os, tuple, std::index_sequence_for<Elems...> {});
    return os << ")";
}

int main(int, char**) {
  std::cout << std::make_tuple("hello", "world!", 1, 2) << "\n";
}


What does line 4 expand to? Especially, what's the order in which the ellipsis expands its' left-hand side? In what conditions is that behavior guaranteed?

Note: the list-initialization hack is there to guarantee the order in which its' contents are evaluated. Without it the elements of the tuple will be printed in an unsequenced order, until C++17, IIRC.

Thanks!
-- Max
Last edited on
Okay -- I figured it out on my own now.

The parameter pack expands the arguments in the order that they appear. That's always the case.
If a parameter pack appears outside a subexpression which contains a parameter pack, the entire subexpression is repeated with each occurrence of the name of the parameter pack replaced by each index, separated by a commas.

In other words, in the program I just posted, line 4 will expand to something equivalent to
1
2
3
4
5
  (void)int[]
    {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 ),
	( void(os << (3 == 0? "" : ", ") << std::get<3>(t)), 0 )};


Which is equivalent to
1
2
3
4
  (void)int[]
    {0, ( void(os << ""   << "hello"), 0 ),
	( void(os << ", " << "world"), 0 ),
	/* ...and so on */};


The inner cast to void doesn't seem to be required, even to silence a warning.
Last edited on
Topic archived. No new replies allowed.