Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I define a concept that is satisfied by an arbitrary std::vector?

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?

like image 873
Maximilian Keßler Avatar asked Jul 25 '21 20:07

Maximilian Keßler


People also ask

What are concepts cpp?

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!

What is concept in c++ 20?

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.


2 Answers

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.
};
like image 200
Barry Avatar answered Nov 15 '22 23:11

Barry


#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.

like image 29
康桓瑋 Avatar answered Nov 15 '22 22:11

康桓瑋