For an exercise for my C++ class (which hasn't covered Boost yet), I am having trouble writing a templated method to accept two iterators for summing numeric values in an STL container.
Consider the following example:
#include <iostream>
#include <iterator>
#include <vector>
template<typename T>
double Sum(const T & c) {
return 42.0; // implementation stubbed
}
// need help writing this method signature to accept two iterators
template<typename T>
double Sum(const typename T::const_iterator & begin,
const typename T::const_iterator & end) {
return 43.0; // another implementation stub
}
int main() {
std::vector<double> v;
v.push_back(3.14);
v.push_back(2.71);
v.push_back(1.61); // sums to 7.46
std::cout << Sum(v) << ' ' // line 23
<< Sum(v.begin(), v.end()) // line 24
<< '\n';
}
I expect this code to output 42 43, but it fails to compile.
The error g++ gives me is:
test_exercise2.cpp: In function ‘int main()’:
test_exercise2.cpp:24: error: no matching function for call to ‘Sum(__gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >, __gnu_cxx::__normal_iterator<double*, std::vector<double, std::allocator<double> > >)’
If I comment out line 24, I get 42 as the output, as expected.
I get the same error message whether or not the second templated method is present or not, so for some reason, it's not able to resolve the call on line 24 to the second method I wrote.
What signature must I have for the method that accepts two iterators?
The reason why I'm stuck on this is because I need to support summing over the second element of std::map<K, V>. This will require two more overloads to call ->second instead of dereferencing the iterator:
1. template<typename K, typename V> double Sum(const std::map<K, V> & m); (I'm okay with this one)
2. and another one involving iterators over the map.
I feel like I'll be able to write the methods for std::map if I can figure out how to specify the passing of iterators for std::list and std::map. I'm okay with solutions that use template-templates.
EDIT: The precise wording of problem (omitting non-contributory sentences).
The containers from "the previous exercise" were std::vector<double>, std::list<double>, std::map<std::string, double>.
Create a template function called Sum() that accepts the template
argument T as input and returns a double. The template argument will
be a container.
In the implementation get an iterator (T::const_iterator) for the end. Then create a loop that iterates the container T and adds all
values. Finally return the sum.
In the main program, call the Sum() function for the different container from the previous exercise.
The Sum() function created calculates the sum of the complete
container. Also create a Sum() function that calculates the sum
between two iterators. The function then uses the template argument
for the iterator type and accepts two iterators, the start and end
iterator.
You're overcomplicating this. You want a pair of iterators of any type? Well, that's just as simple as .. two arguments, of any type.
template<typename Iterator>
double Sum(const Iterator& begin,
const Iterator& end) {
return 43.0; // another implementation stub
}
Problem solved.
By the way, take a hint from the C++ Standard lib: If you can't de-reference the iterator, make the user provide a function to get the value from the iterator. Don't special-case std::map because tomorrow there's std::unordered_map and the day after that boost::multimap and all sorts of fun. And what if I wanted you to sum the keys from the std::map, not the values?
Your hardcoded case is a little more complex. A pair of iterators that have to come from std::map? Not even sure if possible without explicit template arguments.
template<typename K, typename V, typename Comp, typename Alloc>
double Sum(
const std::map<K, V, Comp, Alloc>& map
) { ... }
Notice that I specifically said it had to be a std::map instantiation. This allows the compiler to deduce the parameters. From here, you can access the iterators.
As DeadMG said, the simple way is to template on the type of the iterator. The common convention is, on the other hand, to pass iterators by value:
template <typename Iterator>
double Sum( Iterator begin, Iterator end );
As to why the original code was not working, the problem is that the type of the container is not deducible:
template <typename T>
double Sum( T::const_iterator begin, T::const_iterator end );
Sum( v.begin(), v.end() ); // [*] Assume v == const std::vector<double>&
When the compiler tries to infer the type of the arguments to Sum it only sees the type returned by v.begin() and v.end(), which are the iterator. From that type, it cannot guess the type of the container. To be able to determine what the type T is, it would have to test all non template types, and generate all infinite possible instantiations of template types to look whether they have a nested type const_iterator that matches the type of v.begin() and v.end(). Because that would be impossible, to achieve, the language forbids it in the first place.
Beyond that, and related to the comment [*], even if the type would be deducible, overload resolution is performed on the arguments to the function, and not how the expression is later use. In your program, the argument to .begin() is a std::vector<double> non-const lvalue. Because it is not const, the overload selected will yield a non-const iterator (even if in the function you want to call, there is no need to read it).
The distinguishing feature when contrasting iterators from e.g. std::list with iterators from std::map is that the latter have a pair type as their value_type. That is to say, given std::map<K, V> then both std::map<K, V>::value_type and std::iterator_traits<std::map<K, V>::iterator>::value_type are std::pair<const K, V>.
Hence I suggest your Sum template accept any iterator, but that it operates not on elements given from the iterator (i.e. *it) and instead on a 'view': element(*it). Now you can take care to make sure that element 'does the right thing' when faced with a pair.
As a hint, you could declare Sum as the following (with a bit of metaprogramming for getting the return type correctly):
namespace result_of {
// Second template parameter is an implementation detail
template<
typename Iterator
, typename ValueType = typename std::iterator_traits<Iterator>::value_type
>
struct Sum {
// general case: sum over the value type directly
typedef ValueType type;
};
// If an iterator admits an std::pair as its value_type then we end up here
template<typename Iterator, typename Key, typename Value>
struct Sum<Iterator, std::pair<Key, Value> > {
// special case: sum over the second type of the value
typedef Value type;
};
} // result_of
template<typename Iterator>
typename result_of::Sum<Iterator>::type Sum(Iterator begin, Iterator end);
Related
I'm writing some code that randomly picks an element from a given vector with any generic type. I've successfully made a function that returns a copy of a random element, but it'd be convenient for me if there's a similar function that returns the iterator of that element instead. This is what I have, in which the random iterator is yielded from adding a random number to the begin iterator through a generator function:
template <class T>
typename std::vector<T>::iterator pickElementIterator(const std::vector<T>& v)
{
if (v.empty())
{
// error
return v.end();
}
return v.begin() + generate(v.size());
}
This compiles successfully if no code uses it. But as soon as I try to substitute in some vector with type Foo, the following error comes up using g++:
could not convert '(& v)->std::vector::end()' from '__normal_iterator<const Foo*,[...]>' to
__normal_iterator<Foo*,[...]>'
For both returns. It seems it adds a const to the type when attempting to convert it for returning. I've tried std::decay on both returns but it does not accept those arguments. That's as far as my knowledge on template programming goes, and not much about this is found anywhere I looked.
Note: "typename" is in the return type because otherwise g++ complains with:
need 'typename' before 'std::vector<_RealType>::iterator' because 'std::vector<_RealType>' is a dependent scope
Which is still not clear for me.
std::vector<T>::begin()/std::vector<T>::end() return a const_iterator when const qualified, not an iterator (which is mutable).
The reason this fails to compile is because const_iterators cannot be converted to mutable iterator types. The fix is simple: change the return type to be const_iterator:
template <class T>
typename std::vector<T>::const_iterator pickElementIterator(const std::vector<T>& v)
// ^~~~~~~~~~~~~~
If you're in c++14 or above, you can also just use auto without a declared return type so that it can deduce it itself, e.g.:
template <class T>
auto pickElementIterator(const std::vector<T>& v)
Note: The reason you observed that this compiled successfully when not used is because templates can't fully be evaluated until instantiated. The compiler has to assume that anything that relies on a template parameter (for example, type T) might produce a valid instantiation thanks to template specialization.
For example, the compiler must assume that there may exist a T such that const std::vector<T>::begin() returns something which could construct a std::vector<T>::iterator. This could happen if vector<T> were specialized.
As a result, you won't often see many diagnostics outside of syntax errors until you're actually instantiating a template
I suggest that you let auto decide what kind of iterator that is returned. If you use C++11 you can use a trailing return type like decltype(v.begin()) and in C++14 and later, that's not needed.
If you also make use of template template parameters, you don't have to make it work for vectors only. v.begin() + generate(v.size()) requires random access though, so I suggest using std::next to get the resulting iterator.
Example:
#include <iterator> // std::next
template <template <class, class...> class C, class T, class... Args>
auto pickElementIterator(const C<T, Args...>& c) -> decltype(c.begin()) {
if(c.empty()) return c.end();
return std::next(c.begin(), generate(c.size()));
}
Please take a look at the std::advance function.
According to cppreference the complexity is:
Linear.
However, if InputIt additionally meets the requirements of RandomAccessIterator, complexity is constant.
So if I pass an iterator and an integer, how does the program deduce what iterator it is and how it is implemented? So are there 2 overloads for the function or what?
how program deduce what iterator it and how it is implemented?
It uses std::iterator_traits<T>::iterator_category to determine iterator category and then does tag dispatching.
std::iterator_traits is specialised for raw pointers. Raw pointers are random-access iterators.
E.g.:
template<class I>
void doadvance(I& i, size_t n, std::random_access_iterator_tag) {
i += n;
}
template<class I, class Tag>
void doadvance(I& i, size_t n, Tag) {
while(n--)
++i;
}
template<class I>
void advance(I& i, size_t n) {
using Tag = typename std::iterator_traits<I>::iterator_category;
doadvance(i, n, Tag{});
}
In C++17 instead of tag dispatching it can use if constexpr, but that would make the standard library not backward compatible. This is why it has to keep using tag dispatching.
IMO, std::next has a better interface than std::advance because it returns the iterator, so that the invocation does not have to occupy its own line of code.
how program deduce what iterator it is
std::iterator_traits is used, i.e. std::iterator_traits<It>::iterator_category will be checked, and if it's std::random_access_iterator_tag, means it's a RandomAccessIterator.
BTW: std::iterator_traits is specialized for raw pointers; they're always RandomAccessIterator.
So are there 2 overloads for the function or what?
There're no more overloads of std::advance, the implementation usually implements several helper function overloads, which are called according to the iterator_category in std::advance.
This question already has answers here:
auto it = vector.begin() resulting type is not convertible to const_iterator
(3 answers)
Closed 6 years ago.
Like the question says, I'm wondering about the reason for that. Because I get an error when I try to get the distance between const and non-const iterators.
vector<int> v;
auto it=v.begin();
auto cit=v.cbegin();
distance(it,cit);
no matching function for call to ‘distance(__gnu_cxx::__normal_iterator<int*, std::vector<int> >&, __gnu_cxx::__normal_iterator<const int*, std::vector<int> >&)
From my limited understanding of iterators, I see no reason why it shouldn't work.
You have a mutable iterator and a constant iterator in the call to std::distance, so template argument deduction is failing. You can fix this by specifying the template argument explicitly.
std::vector<int> v;
auto i1 = v.begin();
auto i2 = v.cbegin();
auto dist = std::distance<std::vector<int>::const_iterator>( i1, i2 );
That's because std::distance() only takes one template parameter:
template <class InputIterator>
iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last);
Therefore, first and last must be convertible to the same type, and template resolution will unfortunately not consider that vector<int>::iterator is convertible to vector<int>::const_iterator.
As everyone says, it's because std::distance only takes one iterator type, and template argument deduction is not able to choose which one it should be (even though only one of them is possible given that iterator converts to const_iterator but not back).
It might be worth writing a template something like this:
template <typename Container>
typename Container::const_iterator constify(const Container &, typename Container::iterator it) {
return it;
}
Then you can force the template deduction like this:
std::distance(constify(v, it), cit);
instead of writing out that great long type. The Container& parameter is a shame, it's there because AFAIK Container can't be deduced from an iterator argument alone.
std::distance will work with those two iterators what is not working is the template argument inference. The compiler is trying to resolve what type to substitute for the first template argument and has two potential candidates, which as per the standard ends up in a failure.
You can do one of two things, either compare only iterators of the same type, or provide the type to the template:
std::distance<std::vector<int>::const_iterator>( v.begin(), v.cbegin() );
I was working again with C++ during the weekend and came to notice something that I'm not sure where does it come from.
Following the advice in this thread, I decided to implement a map_keys_iterator and map_values_iterator. I took the -- I think -- recommended-against approach of deriving a class from std::map<K,V>::iterator and implementing it as such:
template <typename K, typename V>
struct map_values_iterator:
public std::map<K,V>::iterator {
// explicitly call base's constructor
typedef typename std::map<K,V>::iterator mIterator;
map_values_iterator (const mIterator& mi) :
mIterator(mi) {};
const V& operator* () const { return (*this)->second; }
};
So far, so good, and the following code works (nvm the Unicode, I default to work with i18n-capable terminals):
typedef std::map<double,string> Map;
Map constants;
constants[M_PI] = "π";
constants[(1+sqrt(5))/2] = "φ";
constants[exp(M_PI)-M_PI] = "fake_20";
// ... fill map with more constants!
map_values_iterator<double, std::string> vs(constants.begin());
for (; vs != m.end(); ++vs) {
cout<< (vs != m.begin() ? ", " : "")<< *vs;
}
cout<< endl;
This code prints the expected result, something like (because a Map's elements are ordered):
..., φ, ..., π, ...., fake_20, ....
So I'd guess a map_keys_iterator would work in a similar way as well. I took the care that a Map's value_type is actually pair<const K, V> so the keys version will return a value.
However, it is unwieldly to have to declare the iterator's type so I wanted to create a caller with the classical make_pair-like idiom. And this is where trouble begins:
template <typename K, typename V>
map_values_iterator<K,V> map_values(const typename std::map<K,V>::iterator &i) {
return lpp::map_values_iterator<K,V> (i);
}
template <typename K, typename V>
map_values_iterator<K,V> map_values(const typename std::map<K,V>::const_iterator &i) {
return lpp::map_values_iterator<K,V> (i);
}
I'm relatively sure this function has the right signature and constructor invocation. However if I attempt to call the function from code:
auto vs= map_values(constants.begin());
I get a single STL compiler error of the form:
error: no matching function for call to ‘map_values(std::_Rb_tree_iterator<std::pair<const double, std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >)’
I'm assuming here that in this particular case the whole _Rb_tree_iterator is actually the correct iterator for which map::iterator is typedefed; I'm not completely sure however. I've tried to provide more overloads to see if one of them matches (drop the reference, drop the const, use only non-const_iterator variants, etc) but so far nothing allows the signature I'm interested in.
If I store the base iterator in a variable before calling the function (as in auto begin= constans.begin(); auto vs= map_values(begin);) I get the exact same base error, only the description of the unmatched call is obviously different (in that it is a "const blah&").
My first attempt at implementing this sort of iterator was by creating a base class that aggregated map::iterator instead of inheriting, and deriving two classes, each with the adequate operator*, but that version ran into many more problems than the above and still forced me to replicate too much of the interface. So I tried this option for code-expedition.
I've tried to look for answers to this issue but my Google-fu isn't very strong today. Maybe I am missing something obvious, maybe I forgot something with the derivation (although I'm almost sure I didn't -- iterators are unlike containers), maybe I am actually required to specify all the template parameters for the map, or maybe my compiler is broken, but whatever it is I can't find it and I am having real trouble understanding what is the actual thing the compiler is complaining about here. In my previous experience, if you are doing something wrong with the STL you are supposed to see a diarrhoea of errors, not only one (which isn't STL to boot).
So... any (well-encapsulated) pointers would be appreciated.
The reason is that your K and V type parameters are in a non-deducible context, so your function template is never even instantiated during overload resolution.
Look at it again:
template <typename K, typename V>
map_keys_iterator<K,V> map_keys(const typename std::map<K,V>::iterator &i)
For this to work, the C++ compiler would somehow have to walk from a specific iterator class to its "parent container" type - map in this case - to get its K and V. In general, this is impossible - after all, a particular iterator might be a typedef for some other class, and the actual type of argument in the call is that other class; there's no way the compiler can "retrace" it. So, per C++ standard, it doesn't even try in this case - and, more generally, in any case where you have typename SomeType<T>::OtherType, and T is a type parameter.
What you can do is make the entire parameter type a template type parameter. This requires some trickery to derive K and V, though.
template <typename Iterator>
map_keys_iterator<
typename std::iterator_traits<Iterator>::value_type::first_type,
typename std::iterator_traits<Iterator>::value_type::second_type
> map_keys(Iterator i)
Unfortunately, you'll have to repeat those two in the body of the function as well, when invoking the constructor of your type.
As a side note, iterators are generally passed by value (they're meant to be lightweight).
I have a class
template <typename Iterator, typename Value>
class Foo {
public:
Foo(const Iterator& it) { ... }
...
private:
map<Value, int> m_;
}
};
Is there any way to get rid of Value in the template? The Iterator may or may not be an STL iterator, but it's guaranteed that *it type is Value.
I know about iterator_traits<T>::value_type for STL iterators, but wonder if there's any way to get Value type automatically for an arbitrary Iterator type?
One trick I'm thinking about - say, we have a helper class
template <typename Iterator, typename Value>
class Bar {
public:
Bar(const Iterator& dummy_iterator, const Value& dummmy_value) {}
...
};
Then if we instantiate Bar as Bar(it, *it), the type of Value will be known inside Bar. But I can't find a good way to combine Bar with Foo.
Any iterator should provide iterator_traits<Iterator>::value_type. If it does not, then it is not an iterator. ISO C++ 2003 24.3.1[lib.iterator.traits] "Iterator traits":
To implement algorithms only in terms
of iterators, it is often necessary to
determine the value and difference
types that correspond to a particular
iterator type. Accordingly, it is
required that if Iterator is the type
of an iterator, the types
iterator_traits<Iterator>::difference_type
iterator_traits<Iterator>::value_type
iterator_traits<Iterator>::iterator_category
be defined as the iterator’s
difference type, value type and
iterator category, respectively.
Aside from that, there's no general way to obtain a type of an arbitrary C++ expression. C++0x will rectify it by providing decltype.
Sorry. The correct way to get rid of Value is to use iterator_traits as you suggested.
If your non-STL iterator is a naked pointer, then you get correct iterator_traits typedefs for free. Otherwise the non-STL iterator class must define the correct typedefs.
See the iterator traits documentation for more information.
As to getting value type of iterator previous answers were correct.
But there is more. The trick you are thinking of would not work with class. If Bar was a function like:
template <typename Iterator, typename Value>
void bar(const Iterator& dummy_iterator, const Value& dummmy_value) {}
then type deduction would work for bar(it, *it) and you would have the value type inside of bar. (But keep in mind that to use this trick you would still have to have a dereferencable iterator it which is not always good - how to deal with empty sequence then?)
Using a class Bar you would have to provide the template arguments Iterator and Value manually as there is no type deduction for classes and using Bar(it, *it) would no compile.