How should I correctly assign cout to a static ostream reference variable?
Asked Answered
A

5

8

I'm defining a class like this:

class StaticRuntimeContext {
 public:
  enum Verbosity {
    kHIGH,
    kMEDIUM,
    kLOW,
    kSILENT
  };
  static void Construct();
  static std::ostream& stdout1() {return stdout1_;}
  static std::ostream& stdout2() {return stdout2_;}
  static std::ostream& stdout3() {return stdout3_;}
  static std::ostream& stderr() {return stderr_;}
 protected:
 private:
  static std::ostream& stdout1_;
  static std::ostream& stdout2_;
  static std::ostream& stdout3_;
  static std::ostream& stderr_;
};

I'm defining the construct function as:

void StaticRuntimeContext::Construct() {
  std::ostream& test = cout;
  stdout1_ = cout;
  stdout2_ = cout;
  //stdout3_ = NULL;
  stderr_ = cerr;
}

I cannot understand why assigning cout to test (std::ostream&) is OK to compile but the compiler produces error messages for the rest like "stdout1_=cout". The error message is:

/usr/lib/gcc/x86_64-redhat-linux/4.6.2/../../../../include/c++/4.6.2/bits/ios_base.h:791:5: error: ‘std::ios_base& std::ios_base::operator=(const std::ios_base&)’ is private

I'm wondering what I should do to correctly assign cout to these ostream reference variables. Thanks!

Amarelle answered 9/11, 2011 at 19:34 Comment(2)
One sidenote: One thing that befuddles me is why "std::ostream& test = cout;" can compile but "stdout_1 = cout" cannot...Amarelle
You can't rebind a reference after its construction, so that's an attempt to copy between the actual streams... which you also can't do.Lordly
P
10

It's because references have value semantics, and the operator = is copying the object instead of assigning a new reference.

Instead of references you should define static pointers, assign them in Construct, and return references in your accessors

  static std::ostream& stdout1() {return *stdout1_;}
  static std::ostream& stdout2() {return *stdout2_;}
  static std::ostream& stdout3() {return *stdout3_;}
  static std::ostream& stderr()  {return *stderr_;}
 protected:
 private:
  static std::ostream* stdout1_;
  static std::ostream* stdout2_;
  static std::ostream* stdout3_;
  static std::ostream* stderr_;

void StaticRuntimeContext::Construct() {
  stdout1_ = &cout;
  stdout2_ = &cout;
  stdout3_ = &cout;
  stderr_ = &cerr;
}

EDIT: And you must add this in your .cpp file

std::ostream* StaticRuntimeContext::stdout1_ = NULL;
std::ostream* StaticRuntimeContext::stdout2_ = NULL;
std::ostream* StaticRuntimeContext::stdout3_ = NULL;
std::ostream* StaticRuntimeContext::stderr_ = NULL;
Piccaninny answered 9/11, 2011 at 19:39 Comment(8)
Still the same question...then why "std::ostream& test = cout;" can pass the compilation then?Amarelle
Because that creates a reference to std::cout, the rest are already created references, so it uses copy-assignment insteadWrongful
@derekhh: std::ostream& test = cout; initialises the reference, and has nothing to do with your other uses of = (which mean completely different things).Lordly
One quick question: After changing the class declaration from references to pointers, compiling seems OK this time, but in linker it produces error messages in forms like: "static_runtime_context.cc:(.text+0x7): undefined reference to `StaticRuntimeContext::stdout1_'"...Amarelle
see the Edit for your last commentPiccaninny
@GabrielCuvillier Er, I'm not so sure that is the case since I've actually defined another static private variable "static boost::timer stopwatch_" in the class. And I've tried to initialize this timer by calling "stopwatch_.restart()" in the StaticRuntimeContext::Construct() function. However, it also provides an error message saying that "static_runtime_context.cc:(.text+0x2d): undefined reference to `StaticRuntimeContext::stopwatch_'"Amarelle
@GabrielCuvillier: And by the way, if I move the implementation of Construct function from the .cc file to the header file, seems everything is OK..strange to me...Amarelle
Got it! :) Static objects must also be declared outside any function or class just like normal globals.Amarelle
S
2

This code

std::ostream& test = cout;

is not an assignment, but a construction of a new reference. It can also be written

std::ostream& test(cout);

without the equal sign. The effect is the same.

A reference cannot be rebound once it is created, so it must be set to its value when created. The static members also have to be defined somewhere, like in the corresponding .cpp file. Just set the values there:

 std::ostream& StaticRuntimeContext::stdout1_ = std::cout;
 std::ostream& StaticRuntimeContext::stdout2_ = std::cout;
 std::ostream& StaticRuntimeContext::stdout3_ = std::clog;
 std::ostream& StaticRuntimeContext::stderr_  = std::cerr;
Sharilyn answered 9/11, 2011 at 20:23 Comment(0)
H
0

In C++ reference variables must be initialized when they are declared.

valid:

int x;
int& foo = x;

Invalid:

int x;
int& foo;
foo = x;
Hautboy answered 9/11, 2011 at 19:42 Comment(1)
Almost but not quite. foo = x is perfectly valid; it just means something else.Lordly
L
0

You need to use a pointer, because references do not allow rebind and std::ostream is noncopyable.

Le answered 9/11, 2011 at 19:44 Comment(0)
F
0

I cannot understand why assigning cout to test (std::ostream&) is OK to compile

Because that isn't assignment; it's initialization.

Footsie answered 9/11, 2011 at 20:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.