syntax to unpack tuple on parameter pack and variadic template

I have an example parameter pack function:

template<typename T, typename ... Args>
constexpr bool all_values_equal(T first, Args... args) {
    return ((first == args) && ...);
}

static_assert(all_values_equal(1, 1, 1.0));

I often use std::tuple as I like to be able to work with templates. Extracting types, Slicing types and other arrangements.

how can I call this function with an std::tuple?

template<typename ... Args>
constexpr bool all_values_equal(std::tuple<Args...> tuple) {
    return /* ??? */ ;
}

static_assert(all_values_equal(std::make_tuple(1, 1, 1.0)));

I would also like know the case for variadic template functions with no arguments:

template<typename T, typename ... Args>
constexpr bool all_types_equal() {
    return (std::is_same_v<T, Args> && ...);
}

template <template<typename ... Args> class Tuple>
constexpr bool all_types_equal() {
    return /* ??? */ ;
}

static_assert(all_types_equal<int, int>());
static_assert(all_types_equal<std::tuple<int, int>>());

ultimately I want to be able to call all 4 variations like this:

static_assert(all_values_equal(1, 1, 1.0));
static_assert(all_values_equal(std::make_tuple(1, 1, 1.0)));
static_assert(all_types_equal<int, int>());
static_assert(all_types_equal<std::tuple<int, int>>());

and the std::tuple functions should not re-implement the logic of the variadic template functions. how can I achieve this in a clean and modern way?

Answer

std::apply let’s you call a function with the tuple elements as parameters. You can use that with a lambda.

template<typename ... Args>
constexpr bool all_values_equal(std::tuple<Args...> tuple) {
    auto cmp = [](auto&& first, auto&&... args) {
        return ((first == args) && ...);
    };

    return std::apply(cmp, tuple);
}

To make the all_types_equal check we can use partial specialization. Something like this.

template <typename First, typename ... Rest>
constexpr bool all_types_equal_impl = (std::is_same_v<First, Rest> && ...);;

template <typename First, typename ... Rest>
constexpr bool all_types_equal_impl<std::tuple<First, Rest...>> = (std::is_same_v<First, Rest> && ...);

template <typename... Args>
constexpr bool all_types_equal() {
    return all_types_equal_impl<Args...>;
}

We can refer to the template variable directly, so wrapping it in a function is not really required if we don’t want to.