Search code examples
c++c++17c++20variadic-templates

Overload variadic function for a single specific type using C++17


I want to overload (or specialize) a variadic function, such that the overload (or specialization) takes only arguments of a specific type. In C++20 I would write:

#include <type_traits>
#include <iostream>

// original version
void foo(auto const&...) { std::cout << "default\n"; }

// oveload/specialization takes only Bar instances
struct Bar {};
void foo(std::same_as<Bar> auto const&...){ std::cout << "Bar\n"; }

The code works as expected. The second version is chosen, if the arguments are all Bar, the first otherwise. How would I create the second overload with C++17? Ideally, I wouldn't have to modify the more general version.

Here is a first failed attempt using std::enable_if: https://godbolt.org/z/9vn39jTd4


Solution

  • You have "typo" in your attempt:

    std::enable_if_t<(std::is_same_v<Bar, Ts> && ...), /* void */> = nullptr is wrong.

    std::enable_if_t<(std::is_same_v<Bar, Ts> && ...), std::nullptr_t> = nullptr would fix the typo.

    Unfortunately, it is insufficient. C++20 has subsumption to have more "specialized" overload.

    With C++17, neither of

    • template <typename... Ts> void bar(Ts const& ...)
    • template <typename... Ts, std::enable_if_t<(std::is_same_v<Bar, Ts> && ...), std::nullptr_t> = nullptr> void bar(Ts const& ...)

    is more specialized.

    Fortunately, you can have:

    template <typename... Ts,
        std::enable_if_t<(std::is_same_v<Bar, Ts> && ...), std::nullptr_t> = nullptr
    >
    void bar(const Bar&, Ts const& ...){ std::cout << "Bar\n"; }
    

    which is a better match.

    Moreover, add overload for empty pack void bar() if you consider no arguments as Bar.

    Demo

    Note: as {} as no type, it can't be deduced, but as first argument of bar IS a Bar, bar({}) would works (whereas foo({}) won't).