Search code examples
c++variadic-templatesparameter-pack

When is an ellipsis needed when dealing with parameter packs?


I'm trying to understand some code from cppreference.com. The relevant part is here

template<typename... Ts>
std::ostream& operator<<(std::ostream& os, std::tuple<Ts...> const& theTuple)
{
    std::apply
    (
        [&os](Ts const&... tupleArgs)
        {
            os << '[';
            std::size_t n{ 0 };
            ((os << tupleArgs << (++n != sizeof...(Ts) ? ", " : "")), ...);
            os << ']';
        }, theTuple
    );
    return os;
}

If I'm interpretting the above correctly ((os << tupleArgs << (++n != sizeof...(Ts) ? ", " : "")), ...) is a fold-expression over the comma operator. Semantically it is([some pattern involving the parameter pack's values] , ...) which means to fold with comma.

What I don't get however is why the sizeof in there is a sizeof...? To me ellipsis means expand, but we don't want to expand there. Ts is like an aggregate type, akin to a tuple, we just want the size at compile time of that aggregate type while the compiler evaluates the fold. And indeed in Visual Studio it works either way, with or without the ellipsis. Same in GCC, Godbolt tells me. (EDIT: actually I am wrong without the ellipsis it compiles but the output contains a trailing comma that should not be there)

Is the rule to just always use sizeof... if you want the size of a parameter pack?


Solution

  • Ellipses are needed there because sizeof...(Ts) is a special form of sizeof that returns the size of a parameter pack. Ordinary sizeof(Ts) could not be used for this purpose because it needs to remain possible to use standard sizeof behavior in expanded patterns i.e. sizeof(Ts) returns the size of an individual type in an evaluated fold expression.