Search code examples
c++rvalue-referenceostream

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

  • Compilers tried: clang, gcc.
  • On Platform: Linux (4.19.0-11-amd64).

Solution

  • 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();
    }