noob_programmer
noob_programmer

Reputation: 37

Template specialization for variadic template

I have two functions, which accept variadic template, and second that accepts std::vector<std::string>. Is it possible, to override variadic templated function without additional arguments? I tried a few things, but it didn't work.

template <typename ... Args>
std::string Func(std::string, Args && ...);

template <typename ArgType = std::string, typename Alloc>
std::string Func(std::string, const std::vector<ArgType, Alloc> &);

int main()
{
  std::vector<std::string> v;
  Func("asd", 1, 2.0f);
  Func("asd", v);
}

Always function with variadic template pack is called. Is the compiler really can't determine the vector specialization? Thanks

Upvotes: 2

Views: 1504

Answers (1)

Ted Lyngmo
Ted Lyngmo

Reputation: 117228

Is the compiler really can't determine the vector specialization?

Yes it can, but it would require a conversion to const std::vector<ArgType, Alloc>& so the first one that doesn't require any conversion is selected instead. If you remove the const part it would select the second version.

You could create a type trait to disqualify the first function if the type is some sort of vector though.

Example:

template<class... Ts>
struct is_first_a_vector {
    static std::false_type test(...); // matches anything

    // this matches only if the first argument is a `vector` of some sort:
    template<template<class,class> class C, class T, class A, class... Rest>
    static auto test(C<T,A>&&, Rest&&...) ->
        std::enable_if_t<std::is_same_v<C<T,A>, std::vector<T,A>>, std::true_type>;

    static constexpr bool value =
        decltype(test(std::declval<std::remove_cvref_t<Ts>>()...))::value;
};

Alternatively:

template <class...>
struct is_first_a_vector_impl : std::false_type {};

template <class T, class A, class... Ts>
struct is_first_a_vector_impl<std::vector<T, A>, Ts...> : std::true_type {};

template <class... Ts>
struct is_first_a_vector
    : is_first_a_vector_impl<std::remove_cv_t<std::remove_reference_t<Ts>>...> {};

Plus a helper that works with both type traits above:

template<class... Ts>
inline constexpr bool is_first_a_vector_v = is_first_a_vector<Ts...>::value;

And here's the type trait used to enable the first function only if the first argument after the std::string is not a vector of some sort:

template <typename... Args>
std::enable_if_t<!is_first_a_vector_v<Args...>, std::string> // <-
Func(std::string, Args&&...);

template <typename ArgType = std::string, typename Alloc>
std::string Func(std::string, const std::vector<ArgType, Alloc> &);

Demo using version 1 and Demo using version 2

Upvotes: 2

Related Questions