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

How to express a constraint in terms of another concept


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!


Solution

  • 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)
    {
        // ...
    }