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.