user2030677
user2030677

Reputation: 3526

Why isn't the template argument deduced?

I'm using istream_iterator to insert contents from my file. I use the std::copy algorithm too.

So we know istream_iterator takes a stream as an argument:

std::istream_iterator<int> it = file;

I can just pass the stream but why here doesn't it work?

#include <fstream>
#include <vector>

int main()
{
    std::ifstream infile("in.txt");
    std::vector<int> v;

    std::copy(infile, std::istream_iterator<int>(), std::back_inserter(v));
}

I have to do it like this:

std::copy(std::istream_iterator<int>(infile), std::istream_iterator<int>(), std::back_inserter(v));

But this looks very verbose and I feel like they're a better way to do what I'm doing...

Upvotes: 1

Views: 161

Answers (3)

David G
David G

Reputation: 96810

This is how the relevant overload of std::copy is declared:

template< class InputIt, class OutputIt >
OutputIt copy( InputIt first, InputIt last, OutputIt d_first );

This function is templatized and it will deduce the type by the argument given. In your case, the first argument is of type std::basic_ifstream<...>, so since the second argument didn't match, you get a compile time error.

That's why you need to be explicit with the type. The compiler won't know what you mean otherwise.

A shorter way of doing this would be to provide the actual template argument explicitly:

v.assign<std::istream_iterator<int>>(infile, {});

Upvotes: 1

leemes
leemes

Reputation: 45675

The problem is what std::copy expects from the object being passed. The algorithm std::copy expects iterators to operate on. To be more precise, the first two arguments have to be "InputIterator"s to read from (start and end), that is, the two objects have to satisfy some "concept".

Sadly, IO streams in C++ don't fulfill the InputIterator concept automatically (also, an end iterator would still be missing), but only if you wrap them in an instance of std::istream_iterator<T>, while the end iterator is symbolically defined using an instance without pointing to a stream.

You could wrap around std::copy which accepts an input stream instead of two iterators (so this also eliminates the need to name the end iterator):

template <class T,
          class OutputIt /* deduced */,
          class CharT    /* deduced */>
OutputIt  copy_from_istream(std::basic_istream<CharT> & stream, OutputIt out) {
    return std::copy(
        std::istream_iterator<T, CharT>(stream),
        std::istream_iterator<T>(),
        out
    );  
}

Using this is now very simple:

copy_from_istream<int>(infile, std::back_inserter(v));

Live Demo

Upvotes: 2

Dmitry Markin
Dmitry Markin

Reputation: 1205

Try using vector::assign() instead of copy() algorithm:

v.assign(std::istream_iterator<int>(infile), std::istream_iterator<int>())

Upvotes: 0

Related Questions