4

I'm writing a library function that takes a list (or "bunch") of items (let's say Student) and does something with them. What's the best way to write the function signature in the interface?

  1. std::vector<Student> The problem with the is that maybe my caller isn't using STL and then they need to instantiate and populate a vector. Not clean and performance suffers.
  2. Student* + numOfStudents doesn't seem right, this isn't C.
  3. A function taking a begin and end iterator? Should it then be implemented as a template function?
3

If the goal is to accept as many different types of "list" as possible, then you should write a template function that accepts anything, and in your implementation either use the begin() and end() free functions to get iterators for it, or use a foreach loop (which the compiler will implement for you using begin() and end()).

This will work for raw arrays, all standard container classes, and all non-standard container classes emulating the standard container interface. In fact, that level of generic-ness is exactly why begin() and end() free functions were added to the language.

If you really need to accept anything, you can still provide additional overloads that take a begin and an end iterator or a pointer and a length.

  • I'd recommend to go the other way round. Implement the function with the most general interface and provide convenience overloads that simply forward to the general function as you see fit. The most general interface here is, as you say, a pair of iterators. A not-so-rare case where std::begin and std::end won't work is if you get a pointer and a length from a C-style function. Not pretty, but also no reason to give up. – 5gon12eder Apr 6 '16 at 21:55
  • @5gon12eder That sounds like the same thing I'm recommending, unless you're trying to say we should leave out the foo(container) overload and provide only foo(begin, end) and foo(pointer, length). In which case I'd probably argue that foo(container) is enough of a readability/convenience gain to justify it. But I'll admit that depends to some extent on the target audience for the library; ime it's far more common to have a container object than a pointer and a length, but maybe you work with more C APIs than I do. – Ixrec Apr 6 '16 at 21:59
  • When I first read your answer, it sounded to me that you're suggesting to always provide the “container version” and optionally also provide the “pair of iterators version”. My remark was to exchange the suggestions. (Admittedly, though, the user of the C-style API could also wrap the pointer and the length in a std::array_view, once we get that.) – 5gon12eder Apr 6 '16 at 22:05
5

Given that you're writing C++, a function template that accepts a pair of iterators for the beginning and (one past the) end would make sense.

Especially if this code is more about the future than the past, I'd consider working with a Range instead. There's a draft Technical Specification for ranges, and a high likelihood that (perhaps a revised form of) it will eventually be added to the standard (perhaps in C++17, possibly delayed until C++20).

If you want to work/play with that a little bit, Eric Neibler has written an implementation (he also wrote most of the proposal).

2

The most C++-like way to pass a range of items is to pass iterators; which as you suggest, involves template parameters.

The reason for this is that a range represented by iterators can be anything, including:

  • A complete container
  • A sub-range within a container
  • A stream
  • Pointers
  • An 'inserter' (e.g. std::back_inserter)
  • std::regex matches
  • Something from one of the POCO or BOOST libraries

If you want your function to be as generic and universally reusable as possible, then iterators are the natural solution; particularly if the only interface you need involves InputIterator concepts (i.e. ++, * and !=).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.