I would like to be able to constrain a C++ function such that the only arguments it can accept are:
std::vector<T>
std::vector<std::vector<T>>
std::vector<std::vector<std::vector<T>>>
...
and so on, accepting an arbitrarily nested std::vector, but with a specific type T
I would like the following to compile:
template<typename T>
requires IsVectorInt<T>
auto foo(T t)
{
}
int main()
{
std::vector<int> intvec {1, 2, 3};
std::vector<std::vector<int>> vecofvec {{1, 2}, {1, 3}};
foo(intvec);
foo(vecofvec);
}
So far I have managed to constrain a function using concepts to only accept either:
std::vector<T>
where T could itself be another std::vector<U>
or:
std::vector<int>
, this however, does not accept any nested vectors.
My code is shown below:
template<class, template<class...> class>
inline constexpr bool is_specialization = false;
template<template<class...> class T, class... Args>
inline constexpr bool is_specialization<T<Args...>, T> = true;
template<class T>
concept IsVector = is_specialization<T, std::vector>;
template<class T>
concept IsVectorInt = IsVector<T> && std::is_same_v<int, typename T::value_type>;
template<typename T>
requires IsVector<T>
auto foo(T t)
{
}
int main()
{
std::vector<int> intvec {1, 2, 3};
std::vector<std::string> vecofstr {"bar", "baz"};
std::vector<std::vector<int>> vecofvec {{1, 2}, {1, 3}};
foo(intvec);
foo(vecofvec);
foo(vecofstr); // I would like this line to fail to compile
}
You can simplify and make your example more readable by creating a trait to find the inner most type of the vector and then using a static_assert
as shown below:
//trait to get the innermost type
template <typename T>
struct inner_type
{
using type = T;
};
//specialization
template <typename T>
struct inner_type<std::vector<T>>
{
using type = typename inner_type<T>::type;
};
//alias for convenience
template<typename T>
using inner_type_t = typename inner_type<T>::type;
template<typename T>
auto foo(std::vector<T> t)
{
static_assert(std::is_arithmetic_v<inner_type_t<T>>);
}
int main()
{
std::vector<int> intvec {1, 2, 3};
std::vector<std::vector<int>> vecofvec {{1, 2}, {1, 3}};
foo(intvec); //works
foo(vecofvec); //works
std::vector<std::string> vecofstr{"bar", "baz"};
//foo(vecofstr); //fails as expected
}
It is trivial to to use require
(or enable_if_t
) instead of static_assert
as shown below:
template<typename T>
auto foo(std::vector<T> t) requires(std::is_arithmetic_v<inner_type_t<T>>)
{
}