Home > Back-end >  Constraint a template parameter to only accept std::vector and std::list with C 20 concepts
Constraint a template parameter to only accept std::vector and std::list with C 20 concepts

Time:09-17

I am trying to write a program that sort only numbers in a std::vector or a std::list for which I did 2 concepts:

template<typename T>
concept ValidContainer = requires(T a) {
    std::same_as<T, std::vector<typename T::value_type>>;
    std::same_as<T, std::list<typename T::value_type>>;
};

And:

template<typename T>
concept Sortable = requires(T a) {
    ValidContainer<T> && std::same_as<typename T::value_type, int>;
    ValidContainer<T> && std::same_as<typename T::value_type, float>;
};

And the signature of the function that sorts them:

void BubbleSort(Sortable auto& collection)
{
    // sort algorithm
}

The question that arises in my head is when I pass a std::vector<std::string> it does not show me a compilation error?

int main()
{
    std::vector<int> test = { 32, 3, 6, 8, 2, 5, 0, 43, 67, 1 };
    std::vector<std::string> test2 = { "first", "second", "third" };

    BubbleSort(test);   // this is ok
    BubbleSort(test2);  // shouldn't this be a compile error?

    return 0;
}

CodePudding user response:

This:

template<typename T>
concept ValidContainer = requires(T a) {
    std::same_as<T, std::vector<typename T::value_type>>;
    std::same_as<T, std::list<typename T::value_type>>;
};

is checking to see if the expression std::same_as<T, std::vector<typename T::value_type>>; is valid, not that it also is true. Which is to say, it's basically just checking that typename T::value_type is a thing.

In that form, if you want to require something, you have to use requires:

template<typename T>
concept ValidContainer = requires(T a) {
    requires std::same_as<T, std::vector<typename T::value_type>>;
    requires std::same_as<T, std::list<typename T::value_type>>;
};

Although now we're requiring that T is both a vector and a list, so that's never going to be true. So really we need:

template <typename T>
concept ValidContainer =
    std::same_as<T, std::vector<typename T::value_type>>
    || std::same_as<T, std::list<typename T::value_type>>;    

Although even this isn't great because now we're rejecting std::vector<T, MyAlloc>.


This is separate from the question of why this is your constraint - why can you only sort vector and list ... but not like deque or string or span or ... ? Or why you're limiting to just int and float?

You can bubble sort any forward range that is orderable. Which I would express as:

template <std::ranges::forward_range R>
    requires std::totally_ordered<std::ranges::range_reference_t<R>>
void BubbleSort(R& r);

CodePudding user response:

requires expressions are true if the enclosed expressions are well-formed. It does not matter if the expressions themselves evaluate to true.

(In fact, the expressions in a requires-expression are never evaluated)

You don't seem to be solving a problem that tests if an expression is well-formed. Your concepts should look like this:

template<typename T>
concept ValidContainer =
    std::same_as<T, std::vector<typename T::value_type>> ||
    std::same_as<T, std::list<typename T::value_type>>;

template<typename T>
concept Sortable = 
    ( ValidContainer<T> && std::same_as<typename T::value_type, int> ) ||
    ( ValidContainer<T> && std::same_as<typename T::value_type, float> );

See it in Compiler Explorer

  • Related