I would like to have a concept
requiring an arbitrary vector as the return type:
template<typename T>
concept HasVector = requires (T t) {
{ T.vec() } -> std::same_as<std::vector<int>>; //works
{ T.vec() } -> std::same_as<std::vector<foo>>; //want to put something arbitrary in here
}
Such that we would have something like the following:
class A {
std::vector<int> vec() { /* ... */}
}
class B {
std::vector<double> vec() { /* ... */}
}
static_assert(HasVector<A>);
static_assert(HasVector<B>);
Moreover, it would be even nicer to require a vector as the return type whose value type satisfies some other concept, i.e.
template<typename T>
concept Arithmetic = // as in the standard
template<typename T>
concept HasArithmeticVector = requires (T t ) {
{ T. vec() } -> std::same_as<std::vector<Arithmetic>>;
Is there such a way to put this in names of concepts?
Concepts are a revolutionary approach for writing templates! They allow you to put constraints on template parameters that improve the readability of code, speed up compilation time, and give better error messages. Read on and learn how to use them in your code!
Writing conceptsConstraint expression can contain constexpr boolean expressions, conjunction/disjunction of other concepts and requires blocks. So for our previous example, we could construct a boolean expression using C++11 type traits or construct a compound concept from C++20 standard library concepts.
We start by writing a variable template to check if a type specializes a template:
template <typename T, template <typename...> class Z>
inline constexpr bool is_specialization_of = false;
template <template <typename...> class Z, class... Args>
inline constexpr bool is_specialization_of<Z<Args...>, Z> = true;
Which we can turn into a concept:
template <typename T, template <typename...> class Z>
concept Specializes = is_specialization_of<T, Z>;
Which we can then use to implement another concept:
template<typename T>
concept HasVector = requires (T t) {
{ t.vec() } -> Specializes<std::vector>;
};
If you want to then do further checking, that's just adding more requirements.
template<typename T>
concept HasVector = requires (T t) {
{ t.vec() } -> Specializes<std::vector>;
// or something along these lines
requires Arithmetic<decay_t<decltype(t.vec()[0])>>;
requires Arithmetic<range_value_t<decltype(t.vec())>>;
// etc.
};
#include <concepts>
#include <vector>
template<typename T>
concept Arithmetic = std::integral<T> || std::floating_point<T>;
template<typename T>
concept HasVector = requires (T t) {
[]<Arithmetic U, typename A>(std::vector<U,A> const&){}(t.vec());
};
Demo.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With