Как мне правильно присвоить cout статическую ссылочную переменную ostream?

Я определяю класс следующим образом:

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_;
};

Я определяю функцию конструкции как:

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

Я не могу понять, почему назначение cout для теста (std::ostream&) допустимо для компиляции, но компилятор выдает сообщения об ошибках для остальных, например «stdout1_=cout». Сообщение об ошибке:

/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

Мне интересно, что я должен сделать, чтобы правильно назначить cout этим ссылочным переменным ostream. Спасибо!


person derekhh    schedule 09.11.2011    source источник
comment
Одно замечание: одна вещь, которая меня сбивает с толку, это то, почему std::ostream& test = cout; компилируется, но stdout_1 = cout не может...   -  person derekhh    schedule 09.11.2011
comment
Вы не можете повторно связать ссылку после ее создания, так что это попытка копирования между фактическими потоками... чего вы также не можете сделать.   -  person Lightness Races in Orbit    schedule 09.11.2011


Ответы (5)


Это связано с тем, что ссылки имеют семантику значений, а оператор = копирует объект вместо назначения новой ссылки.

Вместо ссылок вы должны определять статические указатели, назначать их в 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_;

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

РЕДАКТИРОВАТЬ: И вы должны добавить это в свой файл .cpp

std::ostream* StaticRuntimeContext::stdout1_ = NULL;
std::ostream* StaticRuntimeContext::stdout2_ = NULL;
std::ostream* StaticRuntimeContext::stdout3_ = NULL;
std::ostream* StaticRuntimeContext::stderr_ = NULL;
person Gabriel Cuvillier    schedule 09.11.2011
comment
Тот же вопрос... тогда почему std::ostream& test = cout; может потом пройти компиляцию? - person derekhh; 09.11.2011
comment
Поскольку это создает ссылку на std::cout, остальные уже являются созданными ссылками, поэтому вместо этого используется копирование-назначение. - person Mooing Duck; 09.11.2011
comment
@derekhh: std::ostream& test = cout; инициализирует ссылку и не имеет ничего общего с вашим другим использованием = (что означает совершенно разные вещи). - person Lightness Races in Orbit; 09.11.2011
comment
Один быстрый вопрос: после изменения объявления класса со ссылок на указатели компиляция на этот раз выглядит нормально, но компоновщик выдает сообщения об ошибках в таких формах, как: static_runtime_context.cc:(.text+0x7): неопределенная ссылка на `StaticRuntimeContext::stdout1_ '... - person derekhh; 09.11.2011
comment
см. Редактировать для вашего последнего комментария - person Gabriel Cuvillier; 09.11.2011
comment
@GabrielCuvillier Э-э, я не уверен, что это так, поскольку я фактически определил другую статическую частную переменную static boost::timer stopwatch_ в классе. И я попытался инициализировать этот таймер, вызвав stopwatch_.restart() в функции StaticRuntimeContext::Construct(). Однако он также предоставляет сообщение об ошибке, в котором говорится, что static_runtime_context.cc:(.text+0x2d): неопределенная ссылка на `StaticRuntimeContext::stopwatch_' - person derekhh; 10.11.2011
comment
@GabrielCuvillier: И, кстати, если я перенесу реализацию функции Construct из файла .cc в заголовочный файл, кажется, все в порядке... странно для меня... - person derekhh; 10.11.2011
comment
Понятно! :) Статические объекты также должны быть объявлены вне какой-либо функции или класса, как и обычные глобальные объекты. - person derekhh; 10.11.2011

Этот код

std::ostream& test = cout;

не присваивание, а конструкция новой ссылки. Также можно написать

std::ostream& test(cout);

без знака равенства. Эффект тот же.

Ссылка не может быть переназначена после ее создания, поэтому при создании она должна быть установлена ​​на свое значение. Статические члены также должны быть где-то определены, например, в соответствующем файле .cpp. Просто установите там значения:

 std::ostream& StaticRuntimeContext::stdout1_ = std::cout;
 std::ostream& StaticRuntimeContext::stdout2_ = std::cout;
 std::ostream& StaticRuntimeContext::stdout3_ = std::clog;
 std::ostream& StaticRuntimeContext::stderr_  = std::cerr;
person Bo Persson    schedule 09.11.2011

В C++ ссылочные переменные должны быть инициализированы при их объявлении.

действительный:

int x;
int& foo = x;

Недействителен:

int x;
int& foo;
foo = x;
person Raihan    schedule 09.11.2011
comment
Почти но не совсем. foo = x совершенно верно; это просто означает что-то другое. - person Lightness Races in Orbit; 09.11.2011

Вам нужно использовать указатель, потому что ссылки не допускают повторной привязки, а std::ostream не копируется.

person moshbear    schedule 09.11.2011

Я не могу понять, почему назначение cout для тестирования (std::ostream&) нормально для компиляции

Потому что это не присваивание; это инициализация.

person Karl Knechtel    schedule 09.11.2011