Search code examples
c++templatesvectorc++20c++-concepts

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?


Solution

  • 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.
    };