tripulse
tripulse

Reputation: 1149

Weird behaviour when holding std::ostream rvalue member

#include <iostream>
#include <fstream>

class A {
private:
  std::ostream&& out;  
public:
    A(std::ostream&& o) : out(std::move(o)) {
        out.write("test", 4);
    }
    
    void writeTest2() {
        out.write("test2", 5);
        out.flush();  // still does nothing.
    }
};

int main() {
    A a{std::ofstream{"testic"}};
    a.writeTest2();
}

When the above code is run, it creates a file named testic as expected. However, the file created contains test but not testtest2, which is obviously unexpected. What exactly is causing this behavior?

When std::ostream is taken as a lvalue reference it functions perfectly as intended.

Additional information

Upvotes: 0

Views: 80

Answers (1)

David G
David G

Reputation: 96845

The temporary std::ofstream{"testic"} that you created only exists for the duration of the constructor call. After that it is destroyed and the file is closed, which means you are left with a reference that refers to garbage. Using that reference results in undefined behavior.

To fix it you can remove the reference all together (the && from std::ostream&& out and std::ostream&& o) and have it create a new object that is initialized from the temporary.

The above won't work because std::ostream cannot be moved. You will have to use a pointer instead if you want to maintain polymorphism. If that isn't important you can change all std::ostream&& to std::ofstream:

class A {
 private:
  std::unique_ptr<std::ostream> out;

 public:
  A(std::unique_ptr<std::ostream> o) : out(std::move(o)) {
    out->write("test", 4);
  }

  void writeTest2() {
    out->write("test2", 5);
    out->flush();
  }
};

int main() {
  A a{std::make_unique<std::ofstream>("testic")};
  a.writeTest2();
}

Upvotes: 3

Related Questions