5
ProcessIndex( int index );

template< typename Iterator >
void ProcessIndexes( Iterator start, Iterator end )
{
    while( start!=end )
    {
        ProcessIndex(*start++);
    }
}

How can I enforce that this function can only ever be called with a specific, fixed iterator-value-type, e.g. int (but any container-type) ? In this case, ProcessIndex() takes an int as input, thus, compilation fails for non-primitive types and generates a warning for e.g. float. However, I would like the declaration to enforce int such that the compilation fails for all but int.

Haven't found the "solution" here or elsewhere, despite good efforts, is it trivial (?).

Dr.D.
  • 477
  • 3
  • 11
  • 7
    `static_assert(std::is_same::value_type, int>::value, "ProcessIndexes: Iterators must point to int.");` -- no idea why you'd want that, but hey... – Xeo Jan 11 '13 at 14:47
  • Are you sure a template function and iterator is appropriate in this case. Why not pass a vector of `int` or an array of `int`? – andre Jan 11 '13 at 15:02
  • 2
    Xeo has the right approach, but I'd skip `iterator_traits` and just test `decltype(*start)`. – Ben Voigt Jan 11 '13 at 15:04
  • Re ahenderson: Because the function (actually a class-method) should also accept std::list, std::set, std::map, std::you_name_it :-) I would have liked to pass an arbitrary container with fixed type, but learned that it's commonly done with iterators, which has the added bonus of excepting c-style type-arrays. – Dr.D. Jan 11 '13 at 15:10
  • Thanks Xeo. Unfortunately, right now it does't compile in my settings, though that's a different issue :-) The reason, I'd like the type-restriction here is that the function/method should never be called with anything but `int` as the underlying type and I would like the compiler to complain, if anyone tries :-) – Dr.D. Jan 11 '13 at 15:12
  • @Dr.D. The above suggestions won't work for map, because it has a value_type which is a `std::pair`. Also, what should `ProcessIndex` do for such a pair? It might be best to create a separate specialization for dealing with `std::map` and its ilk. – Agentlien Jan 11 '13 at 15:13
  • @Dr.D.: But the compiler does complain, doesn't it? At any rate, the `static_assert` should ensure that only iterators to `int` are used. You could also go out of the way and complicate the code with SFINAE if you want to further confuse the users with more criptic error messages for zero-gain (I needed to mention this before someone does suggest SFINAE as a solution!) – David Rodríguez - dribeas Jan 11 '13 at 15:15
  • If @Xeo suggestion does not work there are equivalent features available in boost: `BOOST_STATIC_ASSERT()`, `boost::is_same`. – hmjd Jan 11 '13 at 15:17
  • 1
    @Ben: I wouldn't, since `decltype(*start)` is likely `int&` or `int const&`. :) – Xeo Jan 11 '13 at 15:19
  • Seems that MS VS 2008 doesn't have static_assert, and due to software-policies I can't use boost. Good point re std::map, though the function/method would still accept std::set, std::vector, std::list, ..., int-array, which is better than nothing and might just suffice :-) Any suggestion, how to do this via the function-declaration without it getting messy ? – Dr.D. Jan 11 '13 at 15:21
  • @Ben: You can't do that, because various proxy objects and stuff. You have to use `iterator_traits`, because iterators suck. – Puppy Jan 11 '13 at 15:21
  • @Agentlien: Aha. What about `std::map`? `std::list`? Or the worst of all, `std::deque`? Think again. – Xeo Jan 11 '13 at 15:36
  • @Xeo `std::map` is besides the point of this discussion, since its `value_type` won't even be `int`. I agree I made a clumsy statement, didn't think it through entirely. So, I've deleted that comment. What I meant to communicate is that proxy objects won't change the fact that dereferencing an iterator to any standards-conforming container class will give you its `value_type`. – Agentlien Jan 11 '13 at 15:41
  • I don't understand why would one use templates for a function that is supposed to take arguments of fixed types? Why don't you rely solely on function overloading mechanism in that case? – tmaric Jan 11 '13 at 15:57
  • @tomislav-maric The function doesn't take a fixed argument type. It is supposed to take any type of container, only that it should be a container which stores items of a fixed type (int). – Agentlien Jan 11 '13 at 15:59
  • @tomislav-maric: ...excluding containers that don't have the fixed-type as value-type, e.g. std::map :-) So far the static_assert or boost-equivalent is the only practical solution, though unfortunately it doesn't work for me... – Dr.D. Jan 11 '13 at 16:10
  • http://stackoverflow.com/questions/1980012/boost-static-assert-without-boost – TemplateRex Jan 11 '13 at 16:34
  • @rhalbersma: Thanks I'll try that in due course... – Dr.D. Jan 11 '13 at 17:07

2 Answers2

0

Starting from C++20 you can use new concepts and requires keyword to check that the iterator points on int type:

#include <vector>

void ProcessIndex( int ) {}

template< typename Iterator >
void ProcessIndecies( Iterator start, Iterator end )
    requires( std::same_as<std::decay_t<decltype(*start)>, int> )
{
    while( start!=end )
    {
        ProcessIndex(*start++);
    }
}

int main() {
    std::vector<int> vi;
    ProcessIndecies(vi.begin(), vi.end()); //ok

    std::vector<float> vf;
    //ProcessIndecies(vf.begin(), vf.end()); //fails
}

Demo: https://gcc.godbolt.org/z/hba1qh8bz

Fedor
  • 17,146
  • 13
  • 40
  • 131
0

With C++11 you can do this using enable_if, is_same and decay as shown below:

#include<iostream>
#include <type_traits>
#include <vector>

void ProcessIndex( int index )
{
    //do something here
    std::cout<<"Inside ProcessIndex with: "<< index <<std::endl;
}

template< typename Iterator >
auto ProcessIndexes( Iterator start, Iterator end ) -> typename std::enable_if<std::is_same<typename std::decay<decltype(*start)>::type, int>::value>::type
{
    std::cout<<"Inside ProcessIndexes"<<std::endl;
    while( start!=end )
    {
        ProcessIndex(*start++);
    }
}

int main(){
    std::vector<int> vec{1,2,3};
    ProcessIndexes(vec.begin(), vec.end()); //WORKS

    std::vector<float> vec2;
    //ProcessIndexes(vec2.begin(), vec2.end()); //this won't work
}
Jason
  • 36,170
  • 5
  • 26
  • 60