In C++, some STL containers like vector, map, string can be traversed by for loops with colon in it.
for instance:
for(auto c:v)
When I'm writing a custom container, can I make it traversed that way like Java (which only need to implement Iterable)?
Yes, you need to implement some form of iterator and override std::begin(container) and std::end(container) (might work also if you container has begin and end methods).
Internally the code is equivalent to something like the following (this is just to get the point across, the compiler can write it slightly differently, see here for more details).
auto _end = end(v);
for (auto _it = begin(v); _it != _end; ++_it) {
auto c = *_it;
<the rest of loop code>
}
So if your iterator and overrides work as expected it will work for the for loop as well.
You can have a following simple equivalent to Java Iterable interface:
template <typename T, typename U>
struct iterable {
T _begin;
U _end;
iterable(T begin, U end)
: _begin(begin),
_end(end)
{}
T begin() {
return _begin;
}
U end() {
return _end;
}
};
If you wonder why there are T and U when the begin iterator and end iterator should be the same. The reason is that some containers don't have those two iterators of the same type.
Furthermore, you can implement a helper function make_iterable like:
template <typename T, typename U>
iterable<T, U> make_iterable(T t, U u) {
return iterable<T,U>(t, u);
}
Say I want to write a function like this:
template<typename Iterator, typename Iterator_value>
Iterator_value foo(Iterator begin, Iterator end)
{
vector<Iterator*> v(begin, end);
do something
return begin*;
}
This is just a demo, I know there are a lot of bugs. The question is:
how to create a vector with only information about iterators?
how to write a return value for iterator with a template?
Here is an example, I want to write a generic median function, and I want to return the exact value rather than return an iterator. Something like std::find.
Moreover, I don't want to modify raw content, so I want to create a vector to copy iterator values. How can I create a vector with only iterators?
How can I achieve this?
You can use std::iterator_traits
template<typename Iterator>
typename std::iterator_traits<Iterator>::reference
foo(Iterator begin, Iterator end)
{
vector<typename std::iterator_traits<Iterator>::value_type> v(begin, end);
//do something
return *begin;
}
auto and decltype is an alternative:
template<typename Iterator>
decltype(auto) // C++14
foo(Iterator begin, Iterator end)
// or auto foo(..) -> decltype(*begin) in C++11
{
std::vector<std::decay_t<decltype(*begin)>> v(begin, end);
//do something
return *begin;
}
I have a template function inside which I want to generate a vector which is of an unknown type. I tried to make it auto, but compiler says it is not allowed.
The template function gets either iterators or pointers as seen in the test program inside the followed main function. How can the problem be fixed?
template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw domain_error("empty vector");
auto size = distance(beg, end);
vector<auto> temp(size); // <--HERE COMPILER SAYS CANNOT BE AUTO TYPE
copy(beg, end, temp->begin);
.
.
return ....
}
int main()
{
int bips[] = {3, 7, 0, 60, 17}; // Passing pointers of array
auto g = my_func(bips, bips + sizeof(bips) / sizeof(*bips));
vector<int> v = {10, 5, 4, 14}; // Passing iterators of a vector
auto h = my_func(v.begin(), v.end());
return 0;
}
You cannot use a std::vector of auto. You might use std::iterator_traits instead:
std::vector<typename std::iterator_traits<Iter>::value_type> temp(size);
If you have a C++17-compatible compiler, you may profit from class template argument deduction.
So unless you have a specific reason to fill your vector with std::copy, you could write your code like this:
template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw domain_error("empty vector");
vector temp(beg, end);
// do the remaining stuff
return ....
}
If this feature is not available on your compiler, then I'd vote for
vector<typename iterator_traits<Iter>::value_type> temp(beg, end);
like in Jonathan's answer
You might be looking for something like
std::vector<typename std::remove_reference<decltype(*beg)>::type> temp(beg, end);
Demo
The reason auto doesn't work is because it's not allowed in that context. You may not provide auto in place of a template argument. The correct course of action when you want the compiler to deduce a template argument automatically is to not provide an argument at all. However, in this case, there is no way for the compiler to deduce what that type should be. You must provide the type explicitly.
There are many ways of finding out what the correct type for your vector is. You can use std::iterator_traits to obtain information about an iterator, including the type of value it refers to. You would use typename std::iterator_traits<Iter>::value_type.
#include <algorithm>
#include <iterator>
#include <stdexcept>
#include <vector>
template<class Iter>
auto my_func(Iter beg, Iter end)
{
if (beg == end)
throw std::domain_error("empty vector");
auto size = std::distance(beg, end);
using t_value = typename std::iterator_traits<Iter>::value_type;
std::vector<t_value> temp(size);
std::copy(beg, end, temp.begin());
return temp;
}
int main()
{
int bips[] = { 3,7,0,60,17 };//Passing pointers of array
auto g = my_func(bips, bips + sizeof(bips) / sizeof(*bips));
std::vector<int> v = { 10,5,4,14 };//Passing iterators of a vector
auto h = my_func(v.begin(), v.end());
return 0;
}
I would like to point out that there is no reason to check for 0 size ranges. It would correctly return an empty vector.
You can also simplify the body of my_func quite a bit by taking advantage of the fact that std::vector has a constructor that accepts a pair of iterators and copies that range.
template<class Iter>
auto my_func(Iter beg, Iter end)
{
using t_value =typename std::iterator_traits<Iter>::value_type;
return std::vector<t_value>(beg, end);
}
You can extract a pointer/iterator's type information using iterator_traits. value_type is the specific trait that you are interested in, so you can do:
const vector<typename iterator_traits<Iter>::value_type> temp(beg, end);
Live Example
It is not true that the type is not known. The type of the vector you want to create is of the same kind of Iter.
Just get iter Underlying type either using decltype or using iterator type trait as follows:
decltype -> std::vector<typename remove_reference<decltype(*beg)>::type> temp(beg, end);
iterator type trait
as follows
using Type = std::iterator_traits<Iter>::value_type;
std::vector<Type>...
I would solve this slightly differently than your question seems to be asking for.
First, I find ranges to be a better fundamental type than taking two iterators. The two iterators are coupled, they should be one argument. A range is a simple struct of two iterators, with some utility methods:
template<class It>
struct range_t:
std::iterator_traits<It>
{
It b{}, e{};
It begin() const { return b; }
It end() const { return e; }
bool empty() const { return begin()==end(); }
auto size() const { return std::distance(begin(), end()); }
// etc
range_t()=default;
range_t(range_t const&)=default;
range_t(range_t &&)=default;
range_t& operator=(range_t const&)=default;
range_t& operator=(range_t &&)=default;
};
template<class It>
range_t<It> make_range( It s, It f ) { return {std::move(s), std::move(f)}; }
range_ts correctly couple the begin end iterator together.
Now
template<class Range>
auto my_func(Range&& range) {
// todo
}
template<class Iter>
auto my_func(Iter beg, Iter end)
{
return my_func(make_range(std::move(beg), std::move(end)));
}
is the first step. Or just eliminate the two-iterator version entirely, and expect the caller to package up their iterators for you.
template<class Range>
auto my_func(Range&& range) {
if (range.empty())
throw domain_error("empty vector");
// todo
}
Ok, now you want to do this:
auto size = range.size();
vector<auto> temp(size);//<--HERE COMPILER SAYS CANNOT BE AUTO TYPE
copy(range.begin(), range.end(), temp->begin);
but that is a common operation. So we write it for range:
template<class Range>
auto as_vector( Range const& r ) {
using value_type = typename Range::value_type;
std::vector<value_type> v( range.begin(), range.end() );
return v;
}
giving us:
template<class Range>
auto my_func(Range&& range) {
if (range.empty())
throw domain_error("empty vector");
auto v = as_vector(range);
// ...
return ...;
}
We have decomposed your problem into simple primitives that have meaning, and moved implementation complexity into those primitives. The "business logic" of your my_func no longer cares what steps you take to make a range into a vector.
This makes your my_func more readable, so long as you have some trust that as_vector(range) actually returns that range as a vector.
Unfortunately I am not using C++11 (then I would use auto).
Suppose I have a function like the following (very simple example)
template<class ITR>
void f(ITR begin, ITR end)
{
TYPE temp = *begin;
}
I want to store some temp values from the iterators in local variables, but I don't know how to get TYPE. Furthermore, the function will be called with std iterators and raw pointers.
Any help? Thanks
Use std::iterator_traits
template<class ITR>
void f(ITR begin, ITR end)
{
typename std::iterator_traits<ITR>::value_type temp = *begin;
}
typedef typename std::iterator_traits<ITR>::value_type value_type;
I have the following code, which does some iterator arithmetic:
template<class Iterator>
void Foo(Iterator first, Iterator last) {
typedef typename Iterator::value_type Value;
std::vector<Value> vec;
vec.resize(last - first);
// ...
}
The (last - first) expression works (AFAIK) only for random access iterators (like the ones from vector and deque). How can I check in the code that the passed iterator meets this requirement?
If Iterator is a random access iterator, then
std::iterator_traits<Iterator>::iterator_category
will be std::random_access_iterator_tag. The cleanest way to implement this is probably to create a second function template and have Foo call it:
template <typename Iterator>
void FooImpl(Iterator first, Iterator last, std::random_access_iterator_tag) {
// ...
}
template <typename Iterator>
void Foo(Iterator first, Iterator last) {
typedef typename std::iterator_traits<Iterator>::iterator_category category;
return FooImpl(first, last, category());
}
This has the advantage that you can overload FooImpl for different categories of iterators if you'd like.
Scott Meyers discusses this technique in one of the Effective C++ books (I don't remember which one).
In addition to the tag dispatch, you can compare the category to std::random_access_iterator_tag directly using std::is_same_v:
using category = typename std::iterator_traits<Iterator>::iterator_category;
if constexpr (std::is_same_v<category, std::random_access_iterator_tag>) {
vec.resize(last - first);
}
This can sometimes lead to a more clear and concise code, particularly if only a small part of your implementation (like reserving the vector size) depends on the iterator category.