4

I am making a class which is a kind of container and I would like to make a constructor that can take a "first" and "last" iterator like std::vector and other standard containers. What would be the correct syntax ? (I want a template a function that can take any first/last iterator types available (like the standard library I think). Thank you very much !

As an example, I want something like that :

template<class ...> MyClass(... first, ... last) 

But what are the ... ?

Thank you very much.

Regarding the first answer : I want a specific constructor that takes iterators as argument (because I have already constructors that take values and pointers as arguments)

EDIT : Is something like this ok ?

template<class T1, class T2> MyClass(std::iterator<T1, T2> first, std::iterator<T1, T2> last)
Vincent
  • 57,703
  • 61
  • 205
  • 388
  • There is no syntax requirements, but it would be polite of you if it implied the type concept. Something like `InputIterator` or `ForwardIterator`... – K-ballo May 27 '12 at 01:14
  • Regarding your edit: A template can take any type, so you can pass an iterator just fine, I don't understand your problem. – Xeo May 27 '12 at 01:18
  • You mean something similar to this question: http://stackoverflow.com/questions/5075374/iterators-and-templates – helloworld922 May 27 '12 at 01:21
  • I have already a constructor of the form : template MyClass(T1 x, T2 y) and I want a specific constructor for iterators. But I dont know the syntax for declaring a generic iterator... – Vincent May 27 '12 at 01:23
  • You have a big design problem, then. A "generic iterator" type is just a template, as I pointed out in my answer. You could restrict the template so that *only* iterators may get passed, but you have an ambiguity situation with your other constructor then. What is that other constructor used for specifically? – Xeo May 27 '12 at 01:27
  • I add an example in my question. By generic iterator, I mean something derived from a std::iterator. – Vincent May 27 '12 at 01:36
  • There is no `std::iterator` class, and no other common iterator base. – Xeo May 27 '12 at 01:41
  • @Xeo: There is actually an `std::iterator`, but you don't derive from it to get a common sub-type. It's purely for convenience -- it automatically defines all the normal `typedef`s for you (reference_type, value_type, difference_type, etc.) – Jerry Coffin May 27 '12 at 01:47
  • @JerryCoffin: D'oh, right. Nvm my last comment then. – Xeo May 27 '12 at 02:05
  • @Xeo: On a (minor) technicality you were actually correct: `std::iterator` isn't a class, it's a template. There's no common iterator base, because each instantiation of that template over different types is a completely separate type. – Jerry Coffin May 27 '12 at 02:07

2 Answers2

6

The ... can be whatever you want, it's just a placeholder name for whatever the type will be. I think you need to read a good book.

template<class Iter> MyClass(Iter first, Iter last)

Iter is a common name if the type should represent an iterator. Another option might be InIt to signal that the iterators should not be output iterators.

Community
  • 1
  • 1
Xeo
  • 129,499
  • 52
  • 291
  • 397
  • twas not me, but perhaps it should be typename Iter? – helloworld922 May 27 '12 at 01:13
  • 2
    @helloworld922: Nope, `class` and `typename` are synonymous when used for template parameters. Note, however, that you can only use `class` when you have a template template parameter: `template – Xeo May 27 '12 at 01:13
  • @helloworld922, neither is really better than the other per se. The class is just kind of left over. – chris May 27 '12 at 01:14
  • 2
    `class` works fine here. In *Modern C++ Design*, Andre suggests a convention of using `typename` when any type can be passed, and `class` when a user-defined type is required, but that never seems to have really caught on. – Jerry Coffin May 27 '12 at 01:15
3

I think that you can do what you want by taking advantage of the fact that std::iterator's have a member named iterator_category. Combine this with SFINAE and you get something like the following:

#include <iostream>
#include <vector>

template <class X>
class my_class {
public:
    my_class(X a, X b) {
        std::cout << "in my_class(X,X)" << std::endl;
    }

    template <class Iter>
    my_class(Iter a, Iter b, typename Iter::iterator_category *p=0) {
        std::cout << "in my_class(Iter,Iter)" << std::endl;
    }
};

int
main()
{
    char buf[] = "foo";
    std::vector<char> v;

    my_class<int> one(1, 2);
    my_class<char*> two(&buf[0], &buf[3]);
    my_class<char> three(v.begin(), v.end());

    return 0;
}

This prints:

in my_class(X,X)
in my_class(X,X)
in my_class(Iter,Iter)
D.Shawley
  • 58,213
  • 10
  • 98
  • 113
  • Excellent ! I didn't know SFINAE, and it can be very useful – Vincent May 27 '12 at 01:55
  • 1
    Except that pointers are perfectly valid iterators and obviously don't have a `iterator_category` typedef. – Xeo May 27 '12 at 02:04
  • Actually that syntax solves my specific problem, but good point Xeo – Vincent May 27 '12 at 02:07
  • @Xeo _"pointers are valid iterators"_ really depends on your definition of an iterator. If you define it as having the members of `std::iterator`, then a pointer isn't a very good iterator – D.Shawley May 27 '12 at 02:18
  • 1
    `iterator_traits` exists exactly for cases like this. That's why you should use `typename iterator_traits::iterator_category` instead of just `Iter::iterator_category`. – sbabbi May 27 '12 at 02:26
  • @D.Shawley: Except that iterators are modelled after pointers and that's a very important property. – Xeo May 27 '12 at 02:30
  • @sbabbi: Except that you get a hard error when `Iter` is not an iterator, instead of the desired SFINAE soft error. – Xeo May 27 '12 at 02:32