Home > OS >  How to write specialized variadic template for a mix of const and non const types
How to write specialized variadic template for a mix of const and non const types

Time:05-29

I need to specialize a variadic template when the types are a bunch of std::vector<T>. Here's what I have so far

#include <iostream>

#include <vector>
#include <type_traits>

template<typename... Ts>
struct is_vector{
    is_vector(Ts&...){};
    static void print() {std::cout << "not vectors" <<std::endl;}
};

template<typename... Ts>
struct is_vector<std::vector<Ts>...>
{   is_vector(std::vector<Ts>&...){}
    static void print() {std::cout << "vectors" << std::endl;}
};




int main()
{
    
    auto const a3 = std::vector<double> {10., 20., 30.};
    auto a4 = std::vector<int> { 1,2,3};
    
    auto z = is_vector(a4,a3); z.print(); // print "not vectors"
    auto x = is_vector(a4,a4); x.print(); // print "vectors"
    
    return 0;
}

The problem is that when the arguments to is_vector contains a mix of const and not const vectors, it uses the default class rather than the specialized class. Of course I can write a specialization that will apply if all vectors are const, but how to handle a mix is the issue. I have tried this

template<typename... Ts>
struct is_vector<typename std::remove_const<std::vector<Ts> >::type...>
{   is_vector(std::vector<Ts>&...){}
    static void print() {std::cout << "vectors" << std::endl;}
};

but the compiler (Apple clang version 11.0.3 (clang-1103.0.32.62) Target: x86_64-apple-darwin21.4.0) throws this error

test.cpp:13:8: error: class template partial specialization contains a template parameter that cannot be deduced; this partial specialization will never be used [-Wunusable-partial-specialization] struct is_vector<typename std::remove_conststd::vector<Ts>::type...> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ test.cpp:12:22: note: non-deducible template parameter 'Ts' template<typename... Ts> ^ 1 error generated.

CodePudding user response:

It will be easier to separate concerns. Get is_vector working for one input, const or otherwise. Then build the variadic version from this.

For example,

template<class T>
struct is_vector_struct
    : std::false_type {};

template<class T, class allocator>
struct is_vector_struct<std::vector<T, allocator>>
    : std::true_type {};

template<class T>
constexpr bool is_vector = is_vector_struct<std::remove_cvref_t<T>>::value;

static_assert(is_vector<std::vector<int> const&>);

Now use fold expressions for multiple arguments:

template<class...Ts>
constexpr bool all_are_vectors = (is_vector<Ts> && ...);

If you can't use C 17, you can do this using recursion instead.

CodePudding user response:

With C 20 concepts you could go about it like so:

#include <iostream>
#include <vector>
#include <type_traits>

template<typename T>
concept Vector = std::is_same_v<
    std::remove_cvref_t<T>,
    std::vector<typename T::value_type, typename T::allocator_type>
>;

template<typename... Ts>
struct is_vector {
    is_vector(Ts&...) {}
    static void print() {std::cout << "not vectors" <<std::endl;}
};

template<Vector... Ts>
struct is_vector<Ts...> {   
    is_vector(Ts&...) {}
    static void print() {std::cout << "vectors" << std::endl;}
};

int main()
{
    auto const a3 = std::vector<double> {10., 20., 30.};
    auto a4 = std::vector<int> { 1,2,3};
    
    static_assert(Vector<decltype(a3)>);
    static_assert(Vector<decltype(a4)>);
    static_assert(!Vector<int>);

    auto z = is_vector(a4,a3); z.print(); // prints "vectors"
    auto x = is_vector(a4,a4); x.print(); // prints "vectors"
    
    return 0;
}

Note that, depending on the circumstances, you could also take the opportunity to switch on any container that operates like a std::vector by using a broader concept that matches the subset of of the std::vector api your code uses. That would definitely be the more idiomatic approach.

  • Related