Search code examples
c++template-argument-deduction

Template argument deduction failing for const pointers


I don't understand why template argument deduction fails in this case:

template<class Iterator> void
f(Iterator x, const Iterator y) {}

int main()
{
    const int* cpi;
    int* pi;
    f(pi,cpi);
}

Why does Iterator does not deduce to int* for the second parameter?

What would be the most idiomatic declaration for f?


Solution

  • You could create a trait to check if the iterator is an iterator over const values.

    #include <iterator>    // iterator_traits
    #include <type_traits> // is_const_v, remove_pointer_t, enable_if_t
    
    template<typename T>
    struct is_const_iterator {
        // Define a pointer type to the type iterated over. Something already a pointer
        // will still be a pointer
        using pointer = typename std::iterator_traits<T>::pointer;
    
        // Remove the pointer part from the type and check for constness.
        static const bool value = std::is_const_v<std::remove_pointer_t<pointer>>;
    };
    
    // Helper variable template to simplify the use of the above.
    template<class T>
    inline constexpr bool is_const_iterator_v = is_const_iterator<T>::value;
    

    SFINAE use of is_const_iterator_v

    template<class It, class CIt, std::enable_if_t<is_const_iterator_v<CIt>, int> = 0>
    void f(It x, CIt y) {
        //for(; x != y; ++x) std::cout << *x << '\n';
    }
    

    Alternatively, if your function doesn't have other overloads (does not need SFINAE), you can use static_assert to make it even clearer for the user:

    template<class It, class CIt>
    void f(It x, CIt y) {
        static_assert(is_const_iterator_v<CIt>, "Arg 2 must be a const iterator");
        //for(; x != y; ++x) std::cout << *x << '\n';
    }
    

    Example:

    #include <iostream>
    #include <vector>
    
    int main() {
        int values[]{1, 2};
    
        int* pi = values;
        const int* cpi = values + std::size(values);
    
        std::vector<int> v{3, 4};
    
        f(pi, cpi);
        f(v.begin(), v.cend());
        // f(pi, pi);             // error, pi is non-const
        // f(v.begin(), v.end()); // error v.end() is non-const
    }