цепочка Ostream, порядок вывода - PullRequest
10 голосов
/ 19 января 2012

У меня есть функция, которая принимает ссылку ostream в качестве аргумента, записывает некоторые данные в поток, а затем возвращает ссылку на тот же поток, например:

#include <iostream>

std::ostream& print( std::ostream& os ) {
  os << " How are you?" << std::endl;
  return os;
}

int main() {
  std::cout << "Hello, world!" << print( std::cout ) << std::endl;
}

Вывод этого кода:

 How are you?
Hello, world!0x601288

Однако, если я разделю цепочечные выражения на два оператора, как это

int main() {
  std::cout << "Hello, world!";
  std::cout << print( std::cout ) << std::endl;
}

тогда я по крайней мере получаю правильный порядок в выводе, но все равно получаю шестнадцатеричное значение:

Hello, world! How are you?
0x600ec8

Я бы хотел понять, что здесь происходит. Нормальная функция имеет приоритет над operator<<, и поэтому порядок вывода меняется на противоположный? Как правильно написать функцию, которая вставляет данные в ostream, но может также связываться с operator<<?

Ответы [ 5 ]

5 голосов
/ 19 января 2012

Поведение вашего кода не указано согласно стандарту C ++.

Объяснение

следующее (я удалил std::endl для простоты)

std::cout << "Hello, world!" << print( std::cout ); 

эквивалентно этому:

operator<<(operator<<(std::cout, "Hello, World!"), print(std::cout));

, который является вызовом функции, передавая два аргумента:

  • Первый аргумент: operator<<(std::cout, "Hello, World!")
  • Второй аргумент: print(std::cout)

Теперь Стандарт не определяет порядок, в котором оцениваются аргументы. не указано . Но ваш компилятор, кажется, сначала оценивает второй аргумент, поэтому он сначала печатает «Как дела?» , оценивая второй аргумент в значение типа std::ostream&, которое затем передается показанному вызову выше (этим значением является сам объект std::cout).

Почему шестнадцатеричный вывод?

Вы получаете шестнадцатеричный вывод, потому что второй аргумент оценивается как std::cout, который печатается как шестнадцатеричное число, поскольку std::cout неявно преобразуется в значение указателя типа void*, поэтому он печатается как шестнадцатеричное число.

Попробуйте это:

void const *pointer = std::cout; //implicitly converts into pointer type!
std::cout << std::cout << std::endl;
std::cout << pointer << std::endl;

Будет напечатано одинаковое значение для обоих. Например, этот пример на ideone печатает это:

0x804a044
0x804a044 

Также обратите внимание, что я не использовал явное приведение; скорее std::cout неявно преобразуется в тип указателя.

Надеюсь, это поможет.


Как правильно написать функцию, которая вставляет данные в ostream, но может также связываться с operator<<?

Когда это зависит от того, что вы подразумеваете под цепочкой? Очевидно, что следующее не будет работать (как объяснено выше):

std::cout << X << print(std::cout) << Y << Z; //unspecified behaviour!

Независимо от того, как вы пишете print().

Однако это четко определено:

print(std::cout) << X << Y << Z; //well-defined behaviour!
3 голосов
/ 19 января 2012

Причина в том, что ваша функция print () будет оценена перед остальной частью оператора и вернет ссылку на cout, которая затем будет фактически напечатана как указатель (cout << cout). Этот порядок оценки на самом деле не определен, но, похоже, имеет место с вашим компилятором. </p>

Что касается определения "функции" с поддержкой потока, которая на самом деле имеет определенное поведение с той же функциональностью, то это будет работать;

#include <iostream>

template <class charT, class traits>
  std::basic_ostream<charT,traits>& print ( std::basic_ostream<charT,traits>& os )
{
        os << " How are you?" << std::endl;
        return os;
}

int main() {
  std::cout << "Hello, world!" << print << std::endl;
}

См. Также этот ответ для более подробной информации о том, что на самом деле означает "неопределенный" в данном случае.

1 голос
/ 05 марта 2018

Шестнадцатеричный вывод

До C ++ 11 класс std::ostream имеет функцию преобразования в void*. Поскольку ваша функция print возвращает std::ostream&, при оценке std::cout << print(...) возвращаемое значение std::ostream l будет неявно преобразовано в void* и затем выведено как значение указателя. Вот почему существует шестнадцатеричный вывод.

Начиная с C ++ 11, эта функция преобразования заменяется на явную функцию преобразования в bool, поэтому попытка вывести объект std::ostream становится некорректной.

Оценочный заказ

До C ++ 17 перегруженный оператор считается вызовом функции для анализа порядка вычисления, а порядок вычисления различных аргументов вызова функции не определен. Поэтому не удивительно, что функция print вычисляется в первую очередь, поэтому сначала выводится How are you?.

Начиная с C ++ 17, порядок вычисления операндов оператора << строго слева направо, и операнды перегруженного оператора имеют тот же порядок вычисления, что и у встроенного (см. Подробнее * 1027). * здесь ). Таким образом, ваша программа всегда будет получать выходные данные (предположим, что print возвращает что-то, что может быть выведено)

Hello, world! How are you?
something returned by print

ЖИВОЙ ПРИМЕР

1 голос
/ 19 января 2012

Это сработает, чтобы объединить print с << и управлять порядком:

print( std::cout << "Hello, world!" ) << std::endl;

Или, если вы хотите, чтобы функция вызывалась с <<, см. Ответ Иоахима.

1 голос
/ 19 января 2012

В вашем утверждении std::cout << "Hello, world!" << print( std::cout ) << std::endl не определено, произойдет ли std::cout << "Hello, world!" до или после print( std::cout ).Вот почему порядок может быть не таким, как вы ожидаете.

Шестнадцатеричное значение исходит из того факта, что вы также делаете std::cout << std::cout (print возвращает std::cout, который подается в цепочку <<).Правая рука std::cout преобразуется в void * и выводится на выход.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...