To prevent the operator<<() invocations from doing formatting, you should know the streamtype at compile-time. This can be done either with macros or with templates.
My template solution follows.
class NullStream { public: void setFile() { /* no-op */ } template<typename TPrintable> NullStream& operator<<(TPrintable const&) { return *this; } /* no-op */ } } template<class TErrorStream> // add TInfoStream etc class Logger { public: TErrorStream& errorStream() { return m_errorStream; } private: TErrorStream m_errorStream; }; //usage int main() { Logger<std::ofstream> normal_logger; // does real output normal_logger.errorStream().open("out.txt"); normal_logger.errorStream() << "My age is " << 19; Logger<NullStream> null_logger; // does zero output with zero overhead null_logger.errorStream().open("out.txt"); // no-op null_logger.errorStream() << "My age is " << 19; // no-op } Since you have to do this at compile-time, it is of course quite inflexible.
For example, you cannot decide the logging level at runtime from a configuration file.