Search code examples
c++templates

Is there a way of ensuring the template argument passed in C++ is an iterator?


In C++, I am making an implementation of the vector class and came across an error when using any type as the argument for T:

void insert(iterator where, size_type count, const_reference value = T()){...}

template <typename InIt>
void insert(iterator where, InIt first, InIt last){...}

When doing the following:

vector<int> v;
for (int i = 0; i < 5; ++i)
{
    v.push_back(i + 1);
}
v.insert(v.begin() + 2, 10, 8);
v.insert(v.begin() + 4, 9);

It takes the argument for InIt as int, when it should in fact be an iterator type class and means that the wrong function is called, resulting in internal memory errors. As a result, I have had to remove the insert function with the template <typename InIt> argument as it ruins my implementation. I have tried this with std::string and the same problem occurs.

Is there anything I can do to differentiate these two functions?


Solution

  • This is what SFINAE is for - remove the 2nd overload from the overload set if InIt is not something that looks like an iterator. In this case, you just want an input iterator, which has to be both incrementable and dereferencable:

    template <typename InIt,
              typename = decltype(*std::declval<InIt&>(),
                                  ++std::declval<InIt&>())>
    void insert(iterator where, InIt first, InIt last)
    {
        ...
    }
    

    If you were to call insert with some integral type (so that it can be converted to size_t), *std::declval<InIt&> would be an invalid expression since integral types aren't dereferencable - so template deduction would fail. Because Substitution Failure Is Not An Error, the effect is that this overload is removed from consideration, and the first one would be called instead.