Why is the `count()` function overload in `fmt/core.h` implemented like this?

172 Views Asked by At

In fmt/core.h, I noticed the function count_named_args() which uses the template function count with a given predicate.

And I found the overloaded version for count version is weird:

template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
  return (B1 ? 1 : 0) + count<B2, Tail...>();
}

why do we need to take a template parameter bool B2 to extract the next boolean value explicitly, and not the parameter pack bool... Tail directly?

If I remove the bool B2 stuff, and try compile:

template <bool B1, bool... Tail> constexpr auto count() -> size_t {
  return (B1 ? 1 : 0) + count<Tail...>();
}
static_assert(count<false>() == 0);
static_assert(count<true, false>() == 1);

It gives an error as it is ambiguous to determine the overload when the parameter number reduces to the last one:

size_t count<false,>(void) noexcept
size_t count<false>(void) noexcept
1

There are 1 best solutions below

0
On BEST ANSWER

Looking at the current main, there are two function templates:

template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
template <bool B1, bool B2, bool... Tail> constexpr auto count() -> size_t {
  return (B1 ? 1 : 0) + count<B2, Tail...>();
}

If those were instead

template <bool B = false> constexpr auto count() -> size_t { return B ? 1 : 0; }
template <bool B1, bool... Tail> constexpr auto count() -> size_t {
  return (B1 ? 1 : 0) + count<Tail...>();
}

then for call count<false>(), both candidates are considered and as per overload resolution rules neither is more specialized than the other -> ambiguity. Remember, parameter packs can be empty. Why is having an extra parameter pack not considered less specialized, that I honestly do not know.

Only having <bool B1, bool... Tail> would not work for zero arguments and having extra non-template count for this case would not work for corner cases of count<pack...>() and pack being empty. On the other hand <bool B = false> can still be called like count() thanks to the default value.

I think <bool B = false> and <bool B1, bool B2, bool... Tail> is the most generic and simplest solution.