1

I have a very simple function that prints a vector<double> to cout. I have the same function that takes vector<int> instead. Can I replace these with a single function, if possible to take vectors of any type?

void printv(vector<double> vec)
{
    copy(vec.begin(),vec.end(), ostream_iterator<double>(cout," "));
    cout << endl;
}

void printv(vector<int> vec)
{
    copy(vec.begin(),vec.end(), ostream_iterator<int>(cout," "));
    cout << endl;
}

In case someone suggests a solution specifically for printing any type of vector, I have the same issue with a function to save vectors to a file, so the point is the general problem, not specifically to do with printing.

Thanks in advance!

zooombini
  • 143
  • 2
  • 12

3 Answers3

7

Sure. That's what C++ is about.

template<typename T>
void printv(vector<T> const& vec)
{
    copy(vec.begin(), vec.end(), ostream_iterator<T>(cout," "));
    cout << endl;
}

Will work as long as T is "output-streamable".

Note: I've added const& to the signature to avoid the copy.


Now, you might take it a step further:

template<typename Container>
void print(Container const& c)
{
    using std::begin;
    using std::end;
    using std::copy;

    copy(begin(c), end(c), std::ostream_iterator<typename Container::value_type>(cout, " "));
    cout << endl;
}

Making it work for all standard containers, not just the vector.

Bartek Banachewicz
  • 38,596
  • 7
  • 91
  • 135
  • copy might be elided anyway – 4pie0 Sep 26 '14 at 11:21
  • 4
    @0d0a Why rely on might? In many cases it won't. (In fact I can't think any case where it won't copy) – Neil Kirk Sep 26 '14 at 11:21
  • don't rely on might, just know that it might be elided, this is just information – 4pie0 Sep 26 '14 at 11:22
  • 3
    @0d0a Could you give an example where a copy would be elided? – Neil Kirk Sep 26 '14 at 11:23
  • just turn on (Named) Return Value Optimization, call to copy constructor will be elided – 4pie0 Sep 26 '14 at 11:25
  • @od0a and where's the "return value" here (considering this function has a `void` return type)? `const&` can bind to rvalue extending its lifetime, but it can also make use of an lvalue, whereas taking by value has to copy on lvalue and can move from rvalue. C++ isn't really about immutability and the compilers don't make such strong assesments. – Bartek Banachewicz Sep 26 '14 at 11:26
  • 1
    Add `using std::begin; using std::end;` **within** the body of the function adds functionality: C style array support and easier extension to custom range-supporting objects. (direct `std::begin` calls are worse) It is, in my opinion, a best practice. – Yakk - Adam Nevraumont Sep 26 '14 at 11:31
  • @Yakk very good point, thanks. I should remember about that in the future. – Bartek Banachewicz Sep 26 '14 at 11:33
  • @neil easy: a function returning a `vector` that is passed to a function consuming same, the copy is elided. – Yakk - Adam Nevraumont Sep 26 '14 at 12:23
  • That's great, thanks! I want to put these in a source and header file, I read that for some reason I cannot put templates in a source file and have to have them entirely in the header, is this right? If not, how do I declare the template and function in the header, to then as usual put the function contents in the source file? – zooombini Sep 26 '14 at 13:00
  • @zooombini [here, read this](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file) – Bartek Banachewicz Sep 26 '14 at 13:13
  • @zooombini Small template functions like these, just suffer them in a header file. – Neil Kirk Sep 26 '14 at 13:33
2

Yes by using templates:

template<typename T>
void printv(std::vector<T> const &vec)
{
    std::copy(vec.cbegin(),vec.cend(), ostream_iterator<T>(std::cout," "));
    std::cout << std::endl;
}

Alternativelly you could define a template overloaded operator<< for std::vector like below:

template<typename T>
std::ostream& operator<<(std::ostream &out, std::vector<T> const &v) {
  std::copy(v.cbegin(), v.cend(), std::ostream_iterator<T>(out, " "));
  out << std::endl;
  return out;
}

int main() {
  std::vector<int> iv {1, 2, 3, 4, 5};
  std::vector<double> dv {1.1, 1.2, 1.3, 1.4, 1.5};
  std::cout << iv << std::endl;
  std::cout << dv << std::endl;
}

LIVE DEMO

101010
  • 41,839
  • 11
  • 94
  • 168
-2

Try the following

#include <iostream>
#include <vector>
#include <cstring>

template <class T>

std::ostream & print( T &c, std::ostream &os = std::cout )
{
    for ( auto x : c ) os << x << ' ';
    os << std::endl;

    return os;
}

template <class T, size_t N>

std::ostream & print( T ( &a )[N], std::ostream &os = std::cout )
{
    for ( auto x : a ) os << x << ' ';
    os << std::endl;

    return os;
}

template <class T>

std::ostream & print( T *a, size_t n, std::ostream &os = std::cout )
{
    for ( auto p = a; p != a + n; ++p ) os << *p << ' ';
    os << std::endl;

    return os;
}

int main() 
{
    int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    double b[] = { 0.0, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9 };
    char s[] = "Hello zooombini";

    std::vector<int> v1( a, a + sizeof( a ) / sizeof( *a ) );
    std::vector<double> v2( b, b + sizeof( b ) / sizeof( *b ) );

    print( a );
    print( b );
    print( v1 );
    print( v2 );
    print( s, std::strlen( s ) );

    return 0;
}

The output is

0 1 2 3 4 5 6 7 8 9 
0 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 
0 1 2 3 4 5 6 7 8 9 
0 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 
H e l l o   z o o o m b i n i

Or even you can add one more overloaded function

#include <iostream>
#include <vector>
#include <cstring>

template <class T>

std::ostream & print( T &c, std::ostream &os = std::cout )
{
    for ( auto x : c ) os << x << ' ';
    os << std::endl;

    return os;
}

template <class T, size_t N>

std::ostream & print( T ( &a )[N], std::ostream &os = std::cout )
{
    for ( auto x : a ) os << x << ' ';
    os << std::endl;

    return os;
}

template <class T>

std::ostream & print( T *a, size_t n, std::ostream &os = std::cout )
{
    for ( auto p = a; p != a + n; ++p ) os << *p << ' ';
    os << std::endl;

    return os;
}

std::ostream & print( const char *s, std::ostream &os = std::cout )
{
    return os << s << std::endl;
}

std::ostream & print( char *s, std::ostream &os = std::cout )
{
    return os << s << std::endl;
}

int main() 
{
    int a[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    double b[] = { 0.0, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8, 9.9 };
    int *p = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    char s[] = "Hello zooombini";

    std::vector<int> v1( a, a + sizeof( a ) / sizeof( *a ) );
    std::vector<double> v2( b, b + sizeof( b ) / sizeof( *b ) );

    print( a );
    print( b );
    print( p, 10 ) << std::endl;

    print( v1 );
    print( v2 );
    print( s, std::strlen( s ) );
    print( s );

    delete []p;

    return 0;
}

The output is

0 1 2 3 4 5 6 7 8 9 
0 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 
0 1 2 3 4 5 6 7 8 9 

0 1 2 3 4 5 6 7 8 9 
0 1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 
H e l l o   z o o o m b i n i 
Hello zooombini
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335