Alex S.
Alex S.

Reputation: 1214

C++ functions to fill in and print arbitrary container

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

Upvotes: 2

Views: 1149

Answers (1)

bipll
bipll

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

Related Questions