3

There is any specific reason that std::istream_iterator cannot receive the stream as an rvalue?

For passing a temporary object, I have to create a function like:

template<class T, class ostream_t>
std::istream_iterator<T> my_it(ostream_t&& ostream)
{ return {ostream}; }

template<class T>
std::istream_iterator<T> my_it() { return {}; }

int main()
{
    std::string file("3.45 1.23 7,56");

    std::copy(my_it<double>(std::istringstream(file)), my_it<double>(),
              std::ostream_iterator<double>(std::cout, " "));
}

However, that would be more convenient, and shorter:

int main()
{
    std::string file("3.45 1.23 7,56");

    using my_it = std::istream_iterator<double>;

    std::copy(my_it(std::istringstream(file)), my_it(),
              std::ostream_iterator<double>(std::cout, " "));
}

Why, after three standard updates (C++11, C++14 and C++17), there is no rvalue constructor for that kind of iterators, when nearly every any other type have it?

You can argue that, since the iterator is copyable and holds a reference, you can get undefined behaviour if the referenced object is no longer alive (std::reference_wrapper has the rvalue construct disabled as well), but that can also happen with lvalue references.

It's an user land responsability after all.

ABu
  • 10,423
  • 6
  • 52
  • 103
  • Why are you using a singular `ostream` iterator here, rather than an `istringstream` iterator? You're also invoking `my_it` without an argument. Doesn't seem like your example is valid. I understand the underlying question though. – Lightness Races in Orbit Oct 31 '16 at 09:54
  • 1
    http://stackoverflow.com/q/17246720/560648 and http://stackoverflow.com/q/37546443/560648 are questions about this but the answers don't say _why_. Really, you ought to be asking on the relevant committee mailing list. https://m.reddit.com/r/cpp/comments/3109yn/rvalue_constructors_in_iostream_iterator/ gets closer and gives some ideas. On first page of Google results for `istream_iterator rvalue`, FWIW. – Lightness Races in Orbit Oct 31 '16 at 09:56
  • @LightnessRacesinOrbit I forgot the `double` explicit template parameter to the `ostream_iterator`. The `my_it` call without an argument created an `end` iterator. – ABu Oct 31 '16 at 10:19
  • The naming of the type and parameter in that first definition are still very misleading. – Lightness Races in Orbit Oct 31 '16 at 10:20
  • Does your first example work? It looks like UB as `ostream` should be destroyed at the end of the function. – NathanOliver Oct 31 '16 at 11:57
  • @NathanOliver I'm not pretty sure. I've realized that the `ostream_iterator`'s constructor is not explicit, so, I can remove the braces. Anyway, I'm not sure about the behaviour of a list initialization under these specific situation: on a `return { only_one_arg }` statement, `arg` being a reference, the returned type being a non-reference non-aggregate type, and the returned type having an implicit conversion constructor where both, the argument and the parameter are lvalue references. List-initialization rules are complex, because changing any of the properties, changes its semantics. – ABu Oct 31 '16 at 12:20
  • I'm sure that it worked for me after testing, but what I meant is that I'm not fully sure whether it was because I was just lucky, the compiler applied RVO or it is defined behaviour (it's not being moved because it's an lvalue since it has a name, and it's not being copied either because streams are not copyable). – ABu Oct 31 '16 at 12:24
  • 1
    @Peregring-lk Well I made a [simple test](http://coliru.stacked-crooked.com/a/642b775b7ae57c57) and at least on all the compilers I tried it on it was OK so at least there is that. – NathanOliver Oct 31 '16 at 13:00
  • Presumably the same reason `regex_iterator` was made to reject temporary regexes. Too error-prone for the marginal benefit. – T.C. Oct 31 '16 at 20:10
  • @NathanOliver I'm not sure why an `istream_iterator` maker takes something called `ostream`, but that one is well-defined. The temporary is destroyed at the end of the full-expression - i.e., at the `;` after the `std::copy` call`. – T.C. Oct 31 '16 at 20:13
  • @T.C. I'm still not sure why when you leave `my_it` it does not run the destructor for `ostream` since the temporary was moved into the function. – NathanOliver Oct 31 '16 at 20:16
  • 1
    @NathanOliver It wasn't? It takes it by reference, not by value. The destructor for a reference is a no-op. – T.C. Oct 31 '16 at 20:17
  • @T.C. But, usually, or at least, sometimes, when you use a brace list, the arguments are copied to the initialization list, why in this case the argument is taken by reference and passed to the constructor? Or does the behaviour I described take places only when you create a `initialization_list` object? – ABu Oct 31 '16 at 21:14
  • @NathanOliver The `my_it` function took its argument by reference. So, the object pointed-to by the reference is not own by the function and thus destructed. The object destructor is called when its most enclosing scope ends, and a temporary object has a sentence-scope. So, the temporary is deleted when its creating sentence ends. What you cannot do is pass or send a reference outside of the object scope (for example, returning a reference from a local object). – ABu Oct 31 '16 at 21:18
  • I really don't want to repeat the entirety of [\[dcl.init.list\]/3](https://timsong-cpp.github.io/cppwp/dcl.init.list#3) in a comment. – T.C. Oct 31 '16 at 21:22
  • @t.c. doesn't the fact that it takes it by revalue reference move it into the function? – NathanOliver Oct 31 '16 at 21:29
  • @NathanOliver No. And I'm frankly surprised you are actually making this argument. – T.C. Oct 31 '16 at 21:30
  • @NathanOliver Calling a moving constructor happens when you *create* a new object from a temporary (if the type has a constructor taking a rvalue reference, of course), and references are not objects. – ABu Oct 31 '16 at 21:39

1 Answers1

-3

I think your answer is at page 74 of Modern C++:

An istream object, for example, represents a stream of input values, some of which may have already been read, and some of which will potentially be read later. If an istream were to be copied, would that entail copying all the values that had already been read as well as all the values that would be read in the future? The easiest way to deal with such questions is to define them out of existence. Prohibiting the copying of streams does just that.

== EDIT ==

Oh, I totally misinterpreted your question! I now understand what you mean about the temporary value. Let me just paste two version of your program, to highlight the problem.

This does not compile:

#include <string>
#include <iostream>
#include <iterator>
#include <sstream>
#include <algorithm>

int main()
{
    std::string file("3.45 1.23 7,56");

    using my_it = std::istream_iterator<double>;

    std::copy(my_it(std::istringstream(file)), my_it(),
            std::ostream_iterator<double>(std::cout, " "));
}

But this does:

#include <string>
#include <iostream>
#include <iterator>
#include <sstream>
#include <algorithm>

int main()
{
    std::string file("3.45 1.23 7,56");

    using my_it = std::istream_iterator<double>;

    std::istringstream a(file);
    std::copy(my_it(a), my_it(), std::ostream_iterator<double>(std::cout, " "));
}
lbolla
  • 5,387
  • 1
  • 22
  • 35
  • 2
    I don't speak about copying the stream, but about holding a reference (which the iterator already does) to an object passed as rvalue reference (so, to a temporary). – ABu Oct 31 '16 at 10:17