I'm trying to implement a function which allows the user to input some type of begin and end iterator, then all perform some operation on the data. However, the function has to be generic enough it should work for many types of iterators (for example std::vector::iterator, std::string::iterator, std::iterator, etc.). The only limit is that the iterators have to be at least forward_iterator_tag capabilities.
My function prototype looks like this:
template <class key_type, class data_type> std::shared_ptr<data_type>
remove(std::iterator<std::forward_iterator_tag, key_type> key_start,
std::iterator<std::forward_iterator_tag, key_type> key_end);
However, this limits me to using specifically a forward_iterator_tag iterator, so trying to call the function like this:
remove<char, char>(std::iterator<std::random_access_iterator_tag, char>(), std::iterator<std::random_access_iterator_tag, char());
will fail because the compiler can't convert a std::iterator<std::random_access_iterator_tag,...> to a std::iterator<std::forward_access_iterator_tag,...>. Additionally, this method does not work for string iterators, vector iterators, or other stl iterators.
Does someone know how the stl implements the containers/strings to accept iterators from each other? For example, this compiles correctly:
std::string a = "hello";
std::vector<char> v(a.begin(), a.end());
template < typename Iter >
void fun_impl(Iter begin, Iter end, std::forward_iterator_tag)
{
// do your stuff here...
}
template < typename Iter >
void fun(Iter begin, Iter end)
{
fun_impl(begin,end, std::iterator_traits<Iter>::iterator_category());
}
The types returned by begin() and end() for various containers are not of type iterator<category...> but subclasses of such (sometimes). You never, when you're writing generic code, target a specific iterator type. Instead you use "tag dispatching" to classify the iterator and call the correct implementation. Since random_iterator_tag is-a forward_iterator_tag it will be automatically converted to such so that the above fun_impl will resolve correctly for any forward_iterator or extension.
Related
I want to determine the std container type (at least associative or linear) from an iterator passed to a function.
I have searched for appropriate iterator traits and type traits without success.
template <typename RangeIter, typename InputIter>
inline bool filter(RangeIter in_data, InputIter result)
{
...
/* determine the container types here - but how?!? */
std::copy_if(in_data.first, in_data.second, result, /* some predicate code here*/);
...
}
No. (ok, that might be a bit too short for StackOverflow).
There's no way to determine the "container" that an iterator refers to, because they need not refer to a container at all.
Example:
int foo, bar;
std::copy(&foo, &foo+1, &bar);
In this example, none of the iterators passed to std::copy refer to a "container".
For example, c++ std::find_if is implemented like:
template <class InputIter, class UnaryPredicate>
InputIter find_if(InputIter begin, InputIter end, UnaryPredicate pred) {
for (; begin != end; begin++) {
if (pred(*begin))
return begin;
}
return end;
}
InputIter should be std::input_iterator_tag type, but in this function, any type which has operator++ operator++(int) can be compiled.
UnaryPredicate should be something like bool function(const T &value), but any function return int double can be compiled.
Is STL template parameter precise enough ? It seems doesn't check all possible error when compiling.
Can we implement it like:
template <T, Iter<? super std::input_iterator<T>>, Pred<? implement bool (const T&)>>
Iter find_if(Iter begin, Iter end, Pred p) {
for (; begin != end; begin++) {
if (p(*begin))
return begin;
}
return end;
}
In this fake code, I want find_if work for type T, it has range in [begin, end), and use bool (const T &) to check if it is statisfied ?
There are two different questions hiding here. I don't know which one you intend to ask.
First one about find_if. The way find_if is defined explicitly allows the parameter types you want to reject, and that is usually considered as a feature. So implementing checks one way or another would not be conformant and would reject valid code.
The second is about the C++ language. It does not currently offer a way to check or even describe constraints on template parameters more explicitly than by checking usage. The name used in C++ circles for such a system is concept. There are libraries aiming to help checking them (in boost for instance), and there is a Technical Specification which has been implemented in GCC describing how the language could be extended to include a language defined notion of concept, but that TS has not yet been incorporated in the standard.
I've written a lot of code using std::vector<T> and std::vector<T>::iterator. Now I've decided to replace the vector container with a circular buffer from boost, namely boost::circular_buffer<T>.
Of course now the compiler will complain for every function that uses std::... where I'm passing the boost::... counterpart. Do I have to rewrite all functions now? I'm asking since the container from boost works exactly the same. Also the Boost documentation states the following:
The circular_buffer is a STL compliant container. It is a kind of sequence similar to std::list or std::deque.
What does the "STL compliant" part mean? Is it referring to what I would like to do (interchangability) or is it simply a mental note for programmers, that the containers work the same way in boost as in STL?
EDIT: To give an example
class Item{ };
class Queue{
private:
std::vector<Item*> item_vector; // Want to replace only this...
std::vector<Item*>::iterator current_position; // ...and this
public:
Item* get_current_item() const {
return *current_position;
}
std::vector<Item*> get_item_vector(){
return item_vector;
}
};
Do I have to rewrite all functions now?
If your functions specifically use vector and its iterator types, then yes, you will have to change them to use different types.
If they are templates, designed to work with any sufficiently compatible container and iterator types, then they should work without change.
What does the "STL compliant" part mean?
It usually means it follows the specification for an iterable sequence defined by the C++ standard library (which was influenced by the ancient STL library, whose name some people loosely use to refer to some or all of the modern standard library).
For example, it has begin() and end() member functions returning an iterator type; and the iterator types can be incremented with ++ and dereferenced with *.
This means that an algorithm implemented as a template, for example:
template <typename InputIterator, typename T>
InputIterator find(InputIterator begin, InputIterator end, T const & value) {
for (InputIterator it = begin; it != end; ++it) {
if (*it == value) {
return it;
}
}
return end;
}
will work for any iterator type that supports these operations. While a non-generic function
void find(some_iterator begin, some_iterator end, some_type t);
will only work for a single specific iterator type, and will have to be changed or duplicated to support others.
I'm not sure if the answers I was able to find are the easiest way to do what I need. The simple template that I would know how to modify into a full solution to my problem would be code that accomplishes the following:
Takes as input two iterators pointing to the beginning and end of an iterable container (vector, list...) containing things of value type T.
Returns a std::vector<T> containing an element-by-element copy of the input container in whatever order accomplished by iterating the input container from beginning to end.
Something non-functioning would be like follows:
template<typename Iterator, typename T>
std::vector<T> dumb_copy(Iterator first, Iterator last) { ... }
Problem is that I would need the compiler to somehow check that I'm given iterators pointing to something of type T.
I'm currently learning C++ and writing as practice the most generic implementations of certain algorithms that I can think of, so I want to get the best practices right from the start. If there's an easy way of doing this using C++11 constructs, that's fine with me.
You can simply use traits to remove the T type completely, allowing it to be determined automatically:
template <typename Iterator>
std::vector<typename std::iterator_traits<Iterator>::value_type>
dumb_copy(Iterator first, Iterator last)
{
std::vector<typename std::iterator_traits<Iterator>::value_type> copy;
// Populate the copy vector
return copy;
}
In particular, note that std::iterator_traits has a specialization when the iterator type is a pointer, so this will allow your function to "just work" even when it is passed pointers instead of "true" iterator objects.
You don't need to do this because the standard library containers already work this way. So you can create a std::vector from two iterators directly:
#include <string>
#include <vector>
#include <iostream>
int main()
{
std::string s = "hello"; // iterable container
// construct a vector using two iterators
std::vector<std::string::value_type> v(s.begin(), s.end());
// check the results
for(unsigned i = 0; i < v.size(); ++i)
std::cout << v[i];
std::cout << '\n';
}
You can just create a std::vector<T> with a type matching the result of *it for one of the iterators:
#include <type_traits>
#include <vector>
template <typename Iterator>
auto dump_copy(Iterator begin, Iterator end)
-> std::vector<typename std::decay<decltype(*begin)>::type> {
return std::vector<typename std::decay<decltype(*begin)>::type(begin, end);
}
With C++14 you can replace typename std::decay<X>::type by std::decay_t<X>.
I would like to pass arbitrary container as an argument of function and iterate over it (no erasing nor pushing elements). Unfortunately it looks like there is no standard way of doing this.
First solution which comes to my mind is an interface (let's call it CollectionInterface) implemented by classes that will wrap STL containers. so the function declaration would look like:
f(const CollectionInterface * collection);
Or, I was thinking about method template, which has an advantage that it keeps binding at compilation time:
template <class CONTAINER> void f(const CONTAINER & collection);
Which way do you think is better?
ForwardIterator? This is a type of InputIterator (or OutputIterator) that also allows multi-pass algorithms (incrementing it does not invalidate prior values).
Iterators (which are quite different from Java iterators) are the central thread unifying C++ collections. For examples of algorithms working on them (and associated iterator type requirements), you can start with <algorithm>. In particular, search provides an example of using ForwardIterator. It finds the first occurrence within the range [first1, last1] of the sequence defined by the range [first2, last2). These are all objects meeting the requirements of ForwardIterator.
You can also write methods that accept the entire container instead of a reference if that's the way you want to handle things. Iterators into standard library containers are all provided via the member functions begin() and end(), or in some cases rbegin() and rend() for iterating backwards. The way templates work, you don't have to create an actual interface type that objects derive from; the requirements are instead inferred by the object is used.
template<typename Container> void Function(const Container& c) {
for(typename Container::const_iterator i = c.begin(), end = c.end(); i != end; ++i)
//do something
}
Passing iterators provide more flexibility when using the functions, particularly in that not all iterators come from containers with explicit begin() and end() functions, and you can provide whatever explicit subrange you want. But sometimes this method is appropriate.
I would like to pass arbitrary container as an argument of function and iterate over it (no erasing nor pushing elements).
Pass iterators. Here is an example for implementation and use:
template <typename Iter>
void function(Iter begin, Iter end)
{
for (Iter it = begin; it != end; ++it)
{
std::cout << *it << std::endl;
}
}
int main()
{
std::string array[] = {"hello", "array", "world"};
function(array, array + 3);
std::vector<std::string> vec = {"hello", "vector", "world"};
function(vec.begin(), vec.end());
}
Note that in many cases, you don't actually need to write the function, but you can compose it using the library facilities instead and then simply apply std::for_each on that. Or even better, use a preexisting algorithm like std::accumulate or std::find_if.