c++c++-conceptstemplate-argument-deduction

Deduce template parameter using constraint


This appears similar to Deduce template parameter value from concept, but I can't seem to make a functioning solution from the answer there.


I've created a predicate to use with find_if:

template <class Array>
class c_str_array_eq {
    static_assert(std::predicate<c_str_array_eq, const Array &>);

    const char *str;

public:
    explicit constexpr c_str_array_eq(const char *str) : str{str} {}

    bool operator()(const Array &array) const {
        return std::strncmp(str, array.data(), array.size()) != 0;
    }
};

The purpose of this predicate is to search a collection of std::arrays (or array-like objects) for a c-style string.

However when trying to use this predicate, I end up with something like this:

struct my_object {
    std::array<char, 256> str;
}

std::ranges::find_if(a_collection, c_str_array_eq<std::array<char, 256>>("foo"), &my_object::str);

How can I get the compiler to deduce the template parameter to c_str_array_eq in this situation?


Solution

  • Predicates usually aren't templated. Instead, their call operator is templated, because it's the call operator that will trigger argument deduction when called by the standard library algorithms.

    godbolt example

    #include <cstring>
    #include <array>
    #include <vector>
    
    class c_str_array_eq {
        const char *str;
    public:
        explicit constexpr c_str_array_eq(const char *str) : str{str} {}
    
        template <class Array>
        requires requires (const Array& array)
        {
            array.data();
            array.size();
            std::strncmp(str, array.data(), array.size());
        }
        bool operator()(const Array &array) const {
            return std::strncmp(str, array.data(), array.size()) != 0;
        }
    };
    
    struct my_object {
        std::array<char, 256> str;
    };
    
    int main()
    {
        std::vector<my_object> a_collection;
        std::ranges::find_if(a_collection, c_str_array_eq("foo"), &my_object::str);
    }
    

    This is done by almost all standard library predicates like std::less, where the default void template will have a templated operator() that does the actual deduction at the call site.