0

From what I understand, the C++ template class reference_wrapper is useful for references in containers and tuples, because it essentially provides a copy-constructor and assignment operator to normal references. In other words, it seems to be like a pointer that does not allow null.

Based on that understanding, I tried to implement a class that prints strings to std::cout by default but allows the user to override that with a file. This is probably not useful for any real-world program; I just use it here as an example. As an exercise in modern C++, I wanted to avoid using new/delete and rely on RAII features. Here's what the class looks like:

class Printer {
public:
    Printer() : myOut(std::cout)
    {}

    void print(string str)
    {
        myOut.get() << str << endl;
    }

    void setFileOutput(string path)
    {
        fileOutput.reset(new ofstream(path));
        myOut = *fileOutput;
    }

private:
    reference_wrapper<ostream> myOut;
    unique_ptr<ofstream> fileOutput;
};

Everything works as expected, but this is quite a different use case from examples of reference_wrapper I've found online. Is this considered to be a valid use of reference_wrapper? Is there a better alternative that uses modern C++ features such as automatic resource management and RAII?

Caetano Sauer
  • 263
  • 1
  • 10
  • As a matter of fact, the best usage of `std::ref` is with functions which forward their arguments. The most classic case is `std::thread`. I would not advocate for using it to store objects in containers - pointers (smart or raw) are better there. – SergeyA Mar 23 '16 at 17:03
  • 1
    I'm not a big fan, mostly because storing pointers/references into oneself can be error-prone (for example, the moved-from state of your `Printer` is broken). I'd probably use a private helper like `std::ostream& stream(){ return fileOutput ? *fileOutput : std::cout; }` – T.C. Mar 23 '16 at 17:15
  • @SergeyA My first idea was indeed to use a single smart pointer (unique or shared), but I don't think that makes sense because my object does not own `std::cout`, so it should not try to destroy. I guess this would only make sense if `std::cout` would be available as a global `shared_ptr`. – Caetano Sauer Mar 23 '16 at 19:25
  • @CaetanoSauer, don't be afraid of raw pointers. Just have a raw pointer to the stream and maintain it is managed externally. – SergeyA Mar 23 '16 at 19:28
  • You can (ab)use `shared_ptr` for this, just start with a non-owning `shared_ptr` to `cout` and replace it with an owning one. I'm not sure how much I like it, but you can do it. – T.C. Mar 23 '16 at 20:06
  • As well as being error-prone, having one member that exists only to reference another is the pinnacle of redundancy, and not in a good way. I agree with T.C.'s recommendation to just have a member function that returns the appropriate stream. That said: @T.C. What is "a non-owning `shared_ptr`"? How are those created? – underscore_d Sep 23 '18 at 13:37
  • @underscore_d with the aliasing constructor. – T.C. Sep 23 '18 at 17:04

0 Answers0