Use a standard stream, and restore its settings afterwards
I agree with everything what @JiveDadson said in the comment. I believe there are some ways to improve current code.

 ## Syntax

To use the guard, people will need to introduce a scope. My desired syntax would be:

stream &lt;&lt; session &lt;&lt; settings &lt;&lt; data;


I'll present solution with the syntax above later.

My solution seem to solve only a subset of problems which original code does. Thus my point about syntax is not valid. Example can be multiple statement prints, in which case my solution would do unnecessary work.

Naming

Would it be better to use stream_state_guard? The code uses name guard in the tests. It is funny that tests reflect what was actually intended to be written. I found that it is the case in my code too.



Alternative solution

To achieve the syntax above, I've used some of the new features of C++17. My thoughts are a mess more than usual, so watch out for potential nitpicks.

First of all, session needs to be object of some empty type, just like nullptr is to nullptr_t or nullopt is to nullopt_t.

struct session_t {};
static inline constexpr session_t session;


inline is used to deal with linker.

Now, the class itself.

#include &lt;ios&gt;
#include &lt;iomanip&gt;
#include &lt;ostream&gt;

class session_stream
{
    std::ostream&amp; stream;
    std::ios_base::fmtflags flags;
    std::locale locale;
    std::streamsize precision;
    std::streamsize width;
    char fill;

    session_stream(std::ostream&amp; stream)
        : stream{stream},
          flags{stream.flags()},
          locale{stream.getloc()},
          precision{stream.precision()},
          width{stream.width()},
          fill{stream.fill()}
    {}

public:
    friend session_stream operator&lt;&lt;(std::ostream&amp; os, session_t);

    // deleting copy construction also prevents move
    session_stream(const session_stream&amp;) = delete;
    void operator=(const session_stream&amp;) = delete;

    template &lt;typename T&gt;
    std::ostream&amp; operator&lt;&lt;(T&amp;&amp; value)
    {
        stream &lt;&lt; std::forward&lt;T&gt;(value);
        return stream;
    }


    ~session_stream()
    {
        stream.flags(flags);
        stream.imbue(locale);
        stream.precision(precision);
        stream.width(width);
        stream.fill(fill);
    }
};


The code is just adjusted version from the question. Interesting things to note:


Constructor and copy semantics are private

Wrong usage is impossible through conventional means. People will need to be either too smart or cause undefined behavior.
Create an object only by using stream &lt;&lt; session

This might look fishy. Especially this line:

return session_stream{os};


Since C++17 this is guaranteed copy elision, thus it works even if copy constructor is unavailable. I don't think that guarantees impossibility of prolonging lifetime of session_stream, but it will guard against unintentional wrong usages.
Uses its own operator&lt;&lt; only on the first call

I wasn't sure if overload resolution and ADL won't intervene, so I decided to return ostream&amp; on operator&lt;&lt;. 


Rewritten tests:

#include &lt;iostream&gt;

int main()
{
    auto test = []() {
        std::cout &lt;&lt; std::setw(15) &lt;&lt; "Foo" &lt;&lt; ' '
                  &lt;&lt; true &lt;&lt; ' ' &lt;&lt; 123456 &lt;&lt; '\n';
    };
    {
        test();
        std::cout &lt;&lt; session &lt;&lt; std::setfill('_') &lt;&lt; std::left &lt;&lt; std::uppercase
                  &lt;&lt; std::boolalpha &lt;&lt; std::hex &lt;&lt; std::showbase &lt;&lt; std::setw(15) 
                  &lt;&lt; "Foo" &lt;&lt; ' ' &lt;&lt; true &lt;&lt; ' ' &lt;&lt; 123456 &lt;&lt; '\n';
        test();
    } // stream restored here
    test();
    std::cout &lt;&lt; std::endl;

}


Live on Wandbox.

And just for reference: sessions can be nested.