Bérenger
Bérenger

Reputation: 2748

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?

Upvotes: 1

Views: 178

Answers (2)

Ted Lyngmo
Ted Lyngmo

Reputation: 117298

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
}

Upvotes: 1

Vlad from Moscow
Vlad from Moscow

Reputation: 310990

The Iterator can be either int * or const int *. The qualifier const of the second function parameter means either int * const or const int * const.

Upvotes: 1

Related Questions