How to overload a template function to match specific containers? - c++

I have the following function to retrieve the n element of a container - O(n):
template<typename Container>
const typename Container::value_type& getNthElement(const Container& container, size_t n) {
auto itr = cbegin(container);
for (auto i = 0u; i < n; ++i) {
++itr;
}
return *itr;
}
And for vectors I have this overload - O(1):
template<typename T>
T getNthElement(const vector<T>& container, size_t n) {
return container[n];
}
Now if I wanna use a deque (which also has the O(1) implementation), the first template function will be called with the O(n) implementation.
How can the second overload function be adapted to works for vectors and deques ?
My question is taken from this article.

The simple approach is to tag-dipatch based on the iterator category, i.e., something like this:
template <typename It>
typename std::iterator_traits<It>::value_type
nth_element(It begin, It end, std::size_t n, std::input_iterator_tag) {
for (std::size_t i(0); it != end && i != n; ++i) {
++i;
}
return it != end? *it: throw std::runtime_error("out of range");
}
template <typename It>
typename std::iterator_traits<It>::value_type
nth_element(It begin, It end, std::size_t n, std::random_access_iterator_tag) {
return n < std::size_t(end - begin)? it[n]: std::runtime_error("out of range");
}
template <typename C>
typename C::value_type
nth_element(Container const& c, std::size_t n) {
return nth_element(c.begin(), c.end(), n,
typename std::iterator_traits<C>::iterator_category());
}
If it weren't for n possibly being too big, you could actually just have std::advance() do the trick:
template <typename C>
typename C::value_type
nth_element(Container const& c, std::size_t n) {
auto it = c.begin();
std::advance(it, n);
return *it;
}
With C++11 extended SFINAE you can sniff-out whether this capability is available even without traits.

Related

Get total number of elements in a nested STL-like container

I would like to write a C++ function that can count the total number of "atomic" elements in a generic nested STL-like container, with the following conditions:
Each level may be any type of container.
The number of levels is not given a priori.
I wrote the following recursive function (using code from here):
template <typename T>
size_t get_total_size(const T & var)
{
if ( is_container<typeof(var)>::value ) { // From https://stackoverflow.com/a/9407521/2707864
typename T::const_iterator iter;
size_t sumsize = 0;
for ( iter = var.begin() ; iter!= var.end() ; iter++ ) {
sumsize += get_total_size(*iter);
}
return sumsize;
} else {
return 1;
}
};
Compiling/linking this may work.
The problem is that upon using it (otherwise, there wouldn't be any point in writing it!) does not compile, as the instantiation gets at the "atomic" level to a type that does not have iterators, e.g., in this code
typedef vector<int> vint;
typedef vector<vint> vvint;
vvint vec_heap;
for (int i=0; i < 12; i++) {
vec_heap.push_back(vint(2, i));
}
cout << get_total_size(vec_heap) << endl; // Instantiation leads to compilation errors
Is this possible?
EDIT:
As per one comment, it can be done with c++17...
Is the recursive function I wrote an overkill for the objective?
With C++17, you might use if constexpr:
template <typename T>
size_t get_total_size(const T& var)
{
if constexpr (is_container<T>::value) {
return std::accumulate(var.begin(),
var.end(),
0u,
[](int acc, const auto& e){ return acc + get_total_size(e); });
} else {
return 1u;
}
};
Before that, you might use overloads and SFINAE:
// this will be called when T is not a container (it is the "atomic" type)
template <typename T, std::enable_if_t<!is_container<T>::value, int> = 0>
size_t get_total_size(const T& var)
{
return 1u;
};
// this will be called for all container types, except for maps
template <typename T, std::enable_if_t<is_container<T>::value, int> = 0>
size_t get_total_size(const T& var)
{
return std::accumulate(var.begin(),
var.end(),
0u,
[](int acc, const auto& e){ return acc + get_total_size(e); });
};
// this will be called for maps
template <typename Key, typename T>
size_t get_total_size(const std::map<Key, T> & var)
{
return std::accumulate(var.begin(),
var.end(),
0u,
[](int acc, const auto& e){ return acc + get_total_size_sfinae(e.second); });
}
If you can't use C++17, or just want to open up what standard can be used with your function then you can switch to using two overloads and use SFINAE to determine when to call each overload. Using
// this will be called when T is not a container (it is the "atomic" type)
template <typename T, typename std::enable_if<!is_container<T>::value, bool>::type = true>
size_t get_total_size(const T & var)
{
return 1;
}
// forward declare of pair function for associative containers
template <typename T, typename U>
size_t get_total_size(const std::pair<T, U> & var);
// this will be called for all container types
template <typename T, typename std::enable_if<is_container<T>::value, bool>::type = true>
size_t get_total_size(const T & var)
{
size_t sumsize = 0;
for ( auto iter = var.begin() ; iter != var.end() ; ++iter ) {
sumsize += get_total_size(*iter);
}
return sumsize;
}
// this will be called for pair to handle associative containers
template <typename T, typename U>
size_t get_total_size(const std::pair<T, U> & var)
{
return get_total_size(var.first) + get_total_size(var.second);
}
This will work from C++11 on up which you can see in this live example

Passing iterators to templates

Sorry for the unclear question. I need to use the following template to sort an array of object belonging to a custom class using the insertion algorithm:
template<typename pointer, typename T, typename Functype>
void sort_array(pointer puntatore, T* obj, int dim, Functype pred){
T val;
for(int i=1; i<dim; i++){
val=obj[i];
for(int j=(i-1); j>=0; j--){
if(pred(obj[j].*puntatore, val.*puntatore)){
obj[j+1]=obj[j];
obj[j]=val;
}
}
}
}
I'm wondering how I can write a more general template that can accept any kind of iterator that points to an object of class T, not just a pointer. Writing T obj in parameter list gives me troubles with the variable T val in the assignment, which in this case would be something like *val=obj[i] being val itself an iterator. Is there any way to tell the template he has to take a generic iterator pointing to an object of class T(i.e. in the same way writing T* tells it to expect a pointer to an object of class T)?
example of how i might use this template
class Example{
int first;
int second;
};
template<typename pointer, typename T, typename Functype>
void sort_array(pointer puntatore, T* obj, int dim, Functype pred){
T val;
for(int i=1; i<dim; i++){
val=obj[i];
for(int j=(i-1); j>=0; j--){
if(pred(obj[j].*puntatore, val.*puntatore)){
obj[j+1]=obj[j];
obj[j]=val;
}
}
}
}
int main(){
Example array[5]={{1,2},{2,4},{1,7},{5,3},{6,7}};
//now i sort the elements in the array by their first element in a decreasing order
sort_array(&Example::first, array, 5, [](int a, int b){return (a<b);});
}
Well you could take inspiration from STL implementations and provide an interface that would take a range instead of an array like below:
template<typename BidirectionalIterator, typename Predicate =
std::less<typename std::iterator_traits<BidirectionalIterator>::value_type>>
void
insertion_sort(BidirectionalIterator first, BidirectionalIterator last,
Predicate pred = {}) {
if(first != last) {
auto it = first;
while(++it != last) {
auto it2 = it;
while(it2 != first) {
auto it3 = it2;
--it3;
if(pred(*it2, *it3)) {
std::swap(*it2, *it3);
} else {
break;
}
--it2;
}
}
}
}
Live Demo
Mind however, that you could also provide overloaded operator< or operator> for your objects for this to work with standard predicates:
bool
operator<(T const &A, T const &B) {
return A.*puntatore < B.*puntatore;
}
bool
operator>(T const &A, T const &B) {
return A.*puntatore < B.*puntatore;
}

How to do pre-increment on variadic template arguments?

Saw this question, Best STL transform - like template function for ternary operators, thought it would be cool to make an narry transform for kicks and giggles.
So I threw this together, seems to work...with a caviate. My preincrement method is...odd to me. I couldn't figure out how to do with without unpacking. I expected the syntax to be something like (++args)... (similar to the deference syntax of (*args)...) but that returns an error. Anyone know how to do it?
#include <iostream>
#include <vector>
inline void preincrement(){}
template <typename T,typename... Ts>
inline void preincrement(T& t,Ts&... ts)
{
++t;
preincrement(ts...);
}
template <class OutputIterator, class NArryOperator, class InputIterator, class... InputIterators>
OutputIterator transform_n(OutputIterator out_iter, NArryOperator oper, InputIterator iter1begin, InputIterator iter1end,InputIterators... args)
{
for (; iter1begin != iter1end; ++out_iter,++iter1begin,preincrement(args...))
{
*out_iter = oper(*iter1begin,(*args)...);
}
return out_iter;
}
template <typename T>
struct noop
{
T operator()(const T& val){return val;}
};
template <typename Ot,typename T,typename... Ts>
struct nsum
{
Ot operator()(const T& t,const Ts&... ts)
{
return (Ot)t+nsum<Ot,Ts...>()(ts...);
}
};
template <typename Ot, typename T>
struct nsum<Ot,T>
{
Ot operator()(const T& t)
{
return (Ot)t;
}
};
int main(int argc,char** argv)
{
std::vector<int> rng;
for (int i = 0; i < 10; i++) {rng.push_back(i);}
std::vector<int> rng1 = rng;
std::vector<int> rng2 = rng;
std::vector<int> rng3 = rng;
std::vector<float> out(rng.size());
auto beg = out.begin();
auto end = transform_n(beg,nsum<double,int,int,int,int>(),
rng.begin(),rng.end(),
rng1.begin(),
rng2.begin(),
rng3.begin());
for (auto i = beg; i != end; ++i)
{
std::cout << *i << std::endl;
}
}
A solution using postincrement directly:
template <class OutputIterator, class NArryOperator,
class InputIterator, class... InputIterators>
OutputIterator transform_n(OutputIterator out_iter, NArryOperator oper,
InputIterator iter1begin, InputIterator iter1end,
InputIterators... args)
{
while(iter1begin != iter1end)
{
*out_iter++ = oper(*iter1begin++, (*args++)...);
}
return out_iter;
}
About incrementing in the "increment-expression" of a for-statement:
You can't expand in the "increment-expression" of the for-statement; pack expansion is restricted to a few contexts, such that operator, overloads cannot apply [temp.variadic]/4. The usual tricks are required, such as expanding in a function call (what you used) or using a dummy array:
using iarr = int[];
for (; iter1begin != iter1end;
++out_iter,++iter1begin,(void)iarr{0, (++args,void(),0)...})

Pass iterator as a function parameter

I try to write a function, which will sum the elements of a container. This container can be Vector, List, Queue, etc... That's why I tried templates.
Unfortunately I get this error:
'C' is not a template
Source:
#include <iostream>
#include <vector>
using namespace std;
template<class C, typename T>
T sum( C<T>::iterator begin, C<T>::iterator end ) {
T s = null;
for (C<T>::iterator it = begin; it != end; it++) {
s += *it;
}
return s;
}
int main()
{
vector<int> v = {5, 9, 0, 11};
cout << sum(v.begin(), v.end()) << endl;
return 0;
}
What do I wrong? How should I fix it?
You could express the whole thing in terms of a iterator type, and use iterator_traits to get the value_type:
#include <iterator>
template<typename Iterator>
typename std::iterator_traits<Iterator>::value_type
sum(Iterator begin, Iterator end)
{
using value_type = typename std::iterator_traits<Iterator>::value_type;
value_type s = value_type();
for (Iterator it = begin; it != end; it++) {
s += *it;
}
return s;
}
In real life, use std::accumulate:
int sum = std::accumulate(v.begin(), v.end(), 0);
The particular error you get is because you'd need a template template argument:
template<template <typename> class C, typename T>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
T sum( C<T>::iterator begin, C<T>::iterator end )
However, the standard containers typically have more than just one template argument:
template < class T, class Alloc = allocator<T> > class vector
and it is a bit non-trivial to write such function correctly. You could use variadic template arguments, or you could do like the standard library does, and only specialize as much as you really need:
// <algorithm>
namespace std {
template <class RandomAccessIterator>
void sort (RandomAccessIterator first, RandomAccessIterator last);
}
In your case (pretending that your need is not covered by the standard algorithms library already):
template <typename Iterator>
auto sum(Iterator begin, Iterator end)
-> decltype(*begin+*begin) // the type of summing two of them
{
if (begin == end) throw std::logic_error("....");
auto s = *begin;
++begin;
for (; begin != end; ++begin) {
s += *begin;
}
return s;
}
There are some more differences from your original code:
the new code does not assume a null or a default constructor defined (T s = null;)
does not introduce additional iterator (it)
uses pre-increment
throws an exception when begin==end
If you add an init parameter, you can make it almost noexcept:
template <typename Iterator, typename T>
T sum(Iterator begin, Iterator end, T init)
{
for (; begin!=end; ++begin)
init += *begin;
return init;
}
But only almost, because init += *begin could still throw.
If you have such signature, you've by the way reproduced the signature of std::accumulate.

moving elements in a vector is not working as expected

I'm trying to move each element that has x value to the beginning of vector so that all the element that has x value is at the front of the vector, but it is not working , so can you tell me what I've done wrong, please?
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typename Container::iterator it = find(c.begin(), c.end(), x);
if (it!=c.end()) {
c.insert(c.begin(), *it);
remove (it, c.end(), x);
}
}
int main()
{
int x=1;
vector <int> v{1,2,4,6,7,1,3,1,1,8,9};
move_x(v, x);
for(auto i:v)
cout<<v[i];
return 0;
}
and I'm getting this output when I run it
411613848811
Once you insert into the container, the iterator is no longer valid
c.insert(c.begin(), *it); // This invalidates 'it'
remove (it, c.end(), x); // oops! trying to use invalid iterator
Using std::rotate provides better alternative, which doesn't invalidate the iterators:
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typedef typename Container::iterator It;
It write_it = c.begin(), read_it = c.begin();
for (;;) {
It found_it = find(read_it, c.end(), x);
if (found_it==c.end()) break;
read_it = found_it;
++read_it;
std::rotate(write_it,found_it,read_it);
++write_it;
}
}
As long as you are dealing with simple items like ints, this is a good approach:
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typename Container::reverse_iterator it = std::remove(c.rbegin(),c.rend(),x);
for (;it!=c.rend();++it) {
*it = x;
}
}
This is a fixed implementation, of what you had in your code:
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typename Container::iterator it = find(c.begin(), c.end(), x);
if (it!=c.end()) {
c.erase(it);
c.insert(c.end(), x);
}
}
One issue with your implementation is that insert anywhere but the end can cause a reallocation and regardless will invalidate anything after the inserted position. So by definition since you are inserting at the beginning it will not be valid.
The second issue is with cout<<v[i]; it should actually be cout<<i;.
A nicer implementation that uses reverse iterators and moves all xs. This one erases as it goes and keeps a count and then does count inserts when done. Using erase with reverse iterators is a little tricky:
template <typename Container, typename Arg>
void move_all_x(Container& c, Arg x)
{
unsigned int count = 0 ;
for( typename Container::reverse_iterator it = c.rbegin() ; it != c.rend(); )
{
if( *it == x )
{
c.erase(--(it++).base() ) ;
++count ;
}
else
{
++it ;
}
}
for( unsigned int i = 0; i < count; ++i )
{
c.insert(c.begin(), x) ;
}
}
You could use std::partition, which will move all elements in a range which meet a given predicate to the beginning of the range, and return an iterator to the first element which doesn't meet the predicate.
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
typename Container::iterator endrange =
std::partition(c.begin(), c.end(), [&x](Arg ele){ return ele == x; });
}
In this case, we're not using the return value but I think it would probably be useful.
The output is wrong. There should be for (auto i:v) cout << i; not v[i]. You would see the garbage with a right algorithm too
You need a loop to process all matches (or use count and insert). v defined as list<int>:
template <typename Container, typename Arg>
void move_x(Container& c, Arg x)
{
for (auto it = find(c.begin(), c.end(), x);
it != c.end();
it = find(it, c.end(), x)) {
c.insert(c.begin(), x);
it = c.erase(it);
}
}