I am new to using templates and am required to use a template to do something, but don't know how to call the templated function. It's probably soething simple, but I can't see it.
template<class It, class T>
// iterator and value_type of *It
void Calc(It begin, It end, std::pair<int, int> &out)
{
std::vector<It>::iterator iter;
std::map<int, int> StartMap;
std::map<int, int>::reverse_iterator rit;
int sum, start, stop, count;
start = stop = 1;
count = sum = 0;
for(iter = begin; iter != end; iter++ )
{
sum += iter;
count++;
stop++;
if(sum <= 0)
{
// store original start
StartMap.insert(start, count);
// set new start position
start = stop;
}
}
// set iterator to highest value
rit = StartMap.rbegin();
start = rit->first;
stop = start + rit->second;
out.insert(start, stop);
}
but not sure how I call it with 2 std::vector iterators.
I've tried this
void doSomething(std::vector<int>& stopsVec)
{
std::pair<int, int> out;
Calc<std::vector<int>::iterator, std::pair<int, int>>(stopsVec.begin(), stopsVec.end(), &out);
}
void doSomething(std::vector<int>& stopsVec)
{
std::pair<int, int> out;
Calc<std::vector<int>::iterator, std::pair<int, int> >
(stopsVec.begin(), stopsVec.end(), out); // not &out
}
Calc takes a reference to std::pair<int, int>, so you want to just give it out. Passing &out tries to pass a pointer to a pair - which won't work.
EDIT
assuming the signature is actually:
template<class It>
void Calc(It begin, It end, std::pair<int, int> &out)
You can call it with:
Calc(stopsVec.begin(), stopsVec.end(), out);
The compiler can deduce the correct template type from the parameters, without requiring you to specify them between <>
EDIT
Keith makes a good point below. That's another compilation error you would have here. Also note that:
sum += iter;
does not do what you want. you probably meant:
sum += *iter;
But since sum is an int, and iter is a template type, this is not really a general-purpose template method. It's really only going to work for iterators over numeric types.
And, one other issue:
Calc<std::vector<int>::iterator, std::pair<int, int> > // use a space
(stopsVec.begin(), stopsVec.end(), out);
instead of
Calc<std::vector<int>::iterator, std::pair<int, int>> // ">>" is shift operator
(stopsVec.begin(), stopsVec.end(), out);
You need a space between the closing > signs in order to have template syntax. Otherwise you're doing bitshift (or stream extraction), and the compiler will get confused because nothing will make sense from that point on.
Note that:
template<class It, class T>
void Calc(It begin, It end, std::pair<int, int> &out)
{
std::vector<It>::iterator iter;
for(iter = begin; iter != end; iter++ )
is wrong. It should probably be:
template<class It, class T>
void Calc(It begin, It end, std::pair<int, int> &out)
{
It iter;
// etc.
for(iter = begin; iter != end; iter++ )
But also note that in C++, it is generally preferred to follow the 'declaration is initialisation' approach, so this becomes:
template<class It, class T>
void Calc(It begin, It end, std::pair<int, int> &out)
{
// etc.
for(It iter = begin; iter != end; iter++ )
You don't need to explicitly pass the type being iterated over as a template argument. The STL designers were quite wise and realized that this often comes up, and there's a (not very pretty but entirely correct) way to introspect on the type of an iterator to get it's underlying type as follows:
typedef typename std::iterator_traits<It>::value_type value_type;
Once you've done this, you can use the name value_type to refer to the type being iterated over. This lets you rewrite the template function as
template <typename It>
void Calc(It begin, It end, std::pair<int, int>& out) {
typedef typename std::iterator_traits<It>::value_type value_type;
/* ... Rest of the code, now using this type ... */
}
And to seal the deal, now that there aren't any auxiliary types required, you can call the function directly as
std::vector<int> v = /* ... */
std::pair<int, int> result;
Calc(v.begin(), v.end(), result);
Hopefully this is easier to read and write!
Related
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.
This question already has an answer here:
C++ template won't accept iterators
(1 answer)
Closed 9 years ago.
I'm trying to code a template that takes iterators to any type of vector as its arguments. When I try to compile the following, it gives me a no matching function call error.
#include <vector>
struct A { int x; };
template <class T>
void process (typename std::vector<T>::iterator begin,
typename std::vector<T>::iterator end)
{ for(; begin != end; begin++) { /*do_something*/ } }
int main()
{
std::vector <A> obj;
process(obj.begin(), obj.end());
}
1 the type T cannot be deduced from the argument types.
2 Why would you want to restrict the function to only accept iterators to std::vector elements? If you really only want vector elements, better take std::vector<T> const& as argument. But better simply take any iterator arguments (or any container argument).
edit okay, here is an example. You may omit the static_assert, when this becomes identical (apart from the return type) to std::for_each(begin,end,do_something);
template <class It>
void process(It begin, const It end)
{
static_assert(std::is_same<typename std::iterator_traits<It>::iterator_category,
std::random_access_iterator_tag>::value,
"arguments not random access iterators");
for (; begin != end; ++begin)
do_something(*begin);
}
Per the OP's request, see below. you can use any valid container forward iterator that supports value references from operator *() I.e. a vector, deque, list, etc. This does not employ the static assert logic mentioned by chris, I leave that for you to decide.
#include <iostream>
#include <iterator>
template<typename Iterator>
void process(Iterator start, Iterator stop)
{
typedef typename std::iterator_traits<Iterator>::value_type value_type;
for (Iterator it=start; it != stop; ++it)
{
const value_type& val = (*it);
// do something with val
std::cout << val << std::endl;
}
}
int main()
{
int ar[] = { 1,2,3,4,5 };
process(std::begin(ar), std::end(ar));
return 0;
}
Here is a simplified version of my code:
template<typename TIterator>
TIterator findMaximalPosition(TIterator begin, TIterator end)
{
TIterator result(begin);
for (TIterator it = begin + 1; it != end; ++it)
{
if ((*it)->value > (*result)->value) // Here I just need to change to "<"
result = it; // to get a findMinimalPosition
}
return result;
}
template<typename TIterator>
TIterator findMinimalPosition(TIterator begin, TIterator end)
{
// almost the same
}
This is just a simplified example. My code is full of places where two functions are the same except for a < or > sign or whether ++ or -- should be used.
My question is:
Is there a method how to reduce this duplication in code without
Destroying the readability
Decreasing the performance
?
I was thinking of using a pointer to an operator (either < or >) as a template parameter. This should not decrease performance, since the pointer would be a compile time constant. Is there some better or generally used way?
EDIT:
So what I did based on the answers was to implement:
template <typename TIterator, typename TComparison>
TIterator findExtremalPosition(TIterator begin, TIterator end,
TComparison comparison);
and then just call:
return findExtremalPosition(begin, end, std::less<double>());
and
return findExtremalPosition(begin, end, std::greater<double>());
I hope this is what you meant. I suppose that after some struggling similar solution can be done for ++ and -- operators.
I would make a general function that takes a predicate and use std::greater and std::less as argument to that function for the given type to implement findMaximalPosition and findMinimalPosition respectively.
As sugested by Ivaylo Strandjev, one possible solution is to use predicates.
So, if you change your function to work with predicates...
typename std::vector<int> vec;
template<typename TIterator, bool (*Predicate)(const TIterator &, const TIterator &)>
TIterator findPosition(TIterator begin, TIterator end)
{
TIterator result(begin);
for (TIterator it = begin + 1; it != end; ++it)
{
if (Predicate(it, result))
result = it;
}
return result;
}
... and then, you define some predicates that helps you to achieve your goal...
bool lesser(const vec::iterator &a, const vec::iterator &b)
{
return (*a) < (*b);
}
bool greater(const vec::iterator &a, const vec::iterator &b)
{
return (*a) > (*b);
}
... then you would be able to do this:
vec::iterator min = findPosition<typename vec::iterator, lesser>(v.begin(), v.end());
vec::iterator max = findPosition<typename vec::iterator, greater>(v.begin(), v.end());
The advantage is to provide any function you would found useful, not only the ones to check if an element is greater or smaller than other:
bool weird(const vec::iterator &a, const vec::iterator &b)
{
return ((*a) | (*b)) & 0x4;
}
vec::iterator weird = findPosition<typename vec::iterator, weird>(v.begin(), v.end());
Example here.
But before do this effort, check if the Algorithms library has already did the job.
I think that it looks pretty neat and simple.
Hope it helps.
I have the following problem: I need to make a function which takes two iterators and a value and checks if the value is found between the two. The catch: I can only have one template parameter which denotes the type of the elements from the iterators and the value.
My try is like this, but doesn't seem to work:
template <typename T>
T myFind(iterator<std::bidirectional_iterator_tag,T> begin, iterator<std::bidirectional_iterator_tag, T> end, T elem){
// Code
}
But this doesn't work then:
// vector<int> vect; list<string> lst;
myFind(vect.begin(), vect.end(), 15);
myFind(lst.begin(), lst.end(), "some element");
Any ideas?
Code after changes:
template <typename T>
T myFind(T begin, T end,typename std::iterator_traits<T>::value_type elem){
for(T it = begin; it != end; ++it){
if(*it == elem){
return it;
}
}
return end;
}
Can you have one template parameter that is the iterator type? If so:
template <typename It>
typename std::iterator_traits<It>::value_type
myFind(It begin, It end, typename std::iterator_traits<It>::value_type elem){
// ...
}
Otherwise, I think your restriction is too strong.
After your edit: If you want to do - on the iterator that is returned (as you show you do in the comments), you need a random access iterator. However, std::list::iterator is a bidirectional iterator so you can't. You will need to use std::prev (or in C++03, use std::advance).
I am trying to write a template function that will take an STL container and will display all the occurrences of the elements in it and the number that they have occurred. I am planning to use a map, iterate through the container and either add a new element if it does not exist or increment the count of occurrences for the element.
Declaration:
template < typename Container_t >
void findOccurrences (const Container_t& inContainer);
My question is: can I somehow get the type specifier of the element that the container holds?
So when I create my map the key value would be the element in the inContainer.
Something like :
map < typeid ( * inContainer.begin()), int > occurrences;
Or would I have to change my template to something like this:
template < typename Container_t , typename Element_t >
void findOccurrences ( const Container_t & inContainer , Element_t dummy )
{
map < Element_t , int > occurrences;
}
Thanks
How about something like this:
#include <map>
#include <iterator>
template <typename Iter>
void histogram(Iter begin, Iter end)
{
typedef typename std::iterator_traits<Iter>::value_type T;
std::map<T, size_t> h;
while (begin != end) ++h[*begin++];
// now h holds the count of each distinct element
}
Usage:
std::vector<std::string> v = get_strings();
histogram(v.begin(), v.end());
You want typename Container_t::element_type
That is,
std::map <typename Container_t::element_type, int>
With C++0x, it's really easy:
map<decltype(*c.begin()), int> occurrences;
With C++03, you probably need to use a typedef from the container:
template<typename Container>
// ...
map<Container::element_type, int> occurrences;
Please take a look at "RTTI" (run-time type information)
http://en.wikipedia.org/wiki/Run-time_type_information
I hope this helps.