Home > Enterprise >  How to express a constraint in terms of another concept
How to express a constraint in terms of another concept

Time:11-16

It's probably easiest to describe specifically what I'm trying to solve to make this easier to understand.

I have a SmartPointer concept, so that I can have functions which can accept either std::unique_ptr or std::shared_ptr:

template <typename T>
concept SmartPointer = requires(const T& t) {
    requires std::same_as<decltype(t.get()), typename T::pointer>;
};

I want to make a function which can take a pair of iterators, where the iterator value type must be of type SmartPointer and I don't need to explicitly define any types.

I can create a function which has template parameter with the SmartPointer constraint, and check against that:

template <SmartPointer T, std::forward_iterator TIterator, std::sentinel_for<TIterator> TIteratorSentinel>
    requires std::same_as<std::iter_value_t<TIterator>, T>
void doWithSmartPointers(TIterator begin, TIteratorSentinel end) {
    for (auto it = begin; it != end;   it) {
        // Some logic with it->get() etc.
    }
}

However, when using it, I am required to explicitly specify T, which I would prefer not to have to do:

std::vector<std::unique_ptr<int>> v{};
v.push_back(std::make_unique<int>(1));
v.push_back(std::make_unique<int>(2));
v.push_back(std::make_unique<int>(3));
doWithSmartPointer(v.begin(), v.end()); // Error, couldn't infer template argument T
doWithSmartPoint<std::unique_ptr<int>>(v.begin(), v.end()); // OK

From the error message, I'm guessing I need some sort of template deduction guide but as far as I can see they can only be defined for classes/structs and not functions.

I essentially want something like this:

template <std::forward_iterator TIterator, std::sentinel_for<TIterator> TIteratorSentinel>
    requires std::same_as<std::iter_value_t<TIterator>, SmartPointer> // Not valid syntax!
void doWithSmartPointers(TIterator begin, TIteratorSentinel end) {
    for (auto it = begin; it != end;   it) {
        // Some logic with it->get() etc.
    }
}

Am I going about this in the correct way? Is this even possible? Thank you in advance!

CodePudding user response:

You don't need T as a template parameter:

template < std::forward_iterator TIterator
         , std::sentinel_for<TIterator> TIteratorSentinel >
    requires SmartPointer< std::iter_value_t<TIterator> >
    //       ^^^^^^^^^^^^
void whatever(TIterator begin, TIteratorSentinel end)
{
    // ...
}

CodePudding user response:

If you have lot's of functions templated like whatever, it might be worth defining a concept for "iterator pointing to smart pointer"

template <typename T>
concept SmartPointerIterator = requires std::forward_iterator<T> && SmartPointer<std::iter_value_t<T>>;

template < SmartPointerIterator TIterator
         , std::sentinel_for<TIterator> TIteratorSentinel >
void whatever(TIterator begin, TIteratorSentinel end)
{
    // ...
}
  • Related