Reputation: 1214
As I'm learning C++ I would like to write a couple functions to
So far I managed to write the second function one for an arbitrary container, but not the first function.
Print function:
template <class C>
void printMyContainer(C v) {
for (auto it = v.begin(); it != v.end(); it++)
std::cout << std::setw(3) << *it << " ";
std::cout << std::endl;
}
Fill in function:
template <typename T>
using in = typename std::istream_iterator<T>;
template <class C, typename T>
C fillInMyContainer(C v) {
std::cout << "Enter values:"<<std::endl;
std::for_each(in<T>(std::cin), in<T>(),
[&](T a) { v.push_back(a); });
return v;
}
Now, the fill in function has a push_back
method, which is not common for all containers.
Hence I was wondering
how can I pass the fill-in function into the fillInMyContainer
function? I tried to do something like:
template <class C, typename T>
C fillInMyContainer(C v, void (*f)(T)) {
std::for_each(in<T>(std::cin), in<T>(),
[&](T a) { (*f)(a); });
return v;
}
are there already such templates?
Upvotes: 2
Views: 1149
Reputation: 11940
First, printing (the way you do it) can be in a slightly more compact form:
#include <iostream>
#include <iterator>
template<class C> std::ostream &printContainer(std::ostream &s, C const &c) {
std::copy(c.begin(), c.end(), std::ostream_iterator<typename C::value_type>(s, " "));
return s;
}
Though it's not important. Now, for adding elements to arbitrary container you can use the marvels of SFINAE:
template<class C, class T>
decltype(std::declval<C>().insert(std::declval<T>()), C{}) fillContainer(C c) {
std::for_each(in<T>(std::cin), in<T>(), [&c](T const &t){ c.insert(t); });
return c;
}
Or maybe even
template<class C>
decltype(std::declval<C>().insert(std::declval<typename C::value_type>()), std::declval<C &>()) fillContainer(C &c) {
using T = typename C::value_type;
std::for_each(in<T>(std::cin), in<T>(), [&c](T const &t){ c.insert(t); });
return c;
}
What lives inside the parentheses after decltype
is a comma operator in an unevaluated context. Were such an expression found on its own on a separate line, the program would never be built (std::declval
does not have definition, for instance), but merely as an expression to be checked for type-validity it's okay. So, this overload is only chosen when c.insert(t)
compiles (and C{}
after the comma gives us a return type for function.) For vectors (or strings), you can add another:
template<class C, class T>
auto fillContainer(C c) -> decltype(c.push_back(std::declval<T>()), C{});
(Here, I've put the SFINAEing decltype
into a trailing return type, which in this particular case saves us one call to std::declval
, as by this moment we already have c
of type C
, so we don't need to call std::declval
to create a reference to C
.)
Upvotes: 1