Почему вывод приведенной ниже программы такой?
#include <iostream>
using namespace std;
int main(){
cout << "2+3 = " <<
cout << 2 + 3 << endl;
}
производит
2+3 = 15
вместо ожидаемого
2+3 = 5
Этот вопрос уже прошел несколько циклов закрытия / повторного открытия.
Перед тем как проголосовать за закрытие, пожалуйста, подумайте об этом мета-обсуждении этой проблемы.
;
в конце первой выходной строки, а не<<
. Вы не печатаете то, что вы думаете, что печатаете. Вы делаете тоcout << cout
, что печатаете1
(какcout.operator bool()
мне кажется). Затем сразу следует5
(от2+3
), делая его похожим на число пятнадцать.Ответы:
Умышленно или случайно у вас есть
<<
в конце первой выходной строки, где вы, вероятно, имели в виду;
. Так что по сути у вас естьИтак, вопрос сводится к следующему: зачем
cout << cout;
печатать"1"
?Это, как ни удивительно, оказывается тонким.
std::cout
через свой базовый классstd::basic_ios
предоставляет определенный оператор преобразования типа, который предназначен для использования в логическом контексте, как вЭто довольно плохой пример, так как трудно получить выход из строя, но
std::basic_ios
на самом деле он является базовым классом как для входных, так и для выходных потоков, а для входных данных он имеет гораздо больше смысла:(выходит из цикла в конце потока или когда символы потока не образуют действительное целое число).
Теперь точное определение этого оператора преобразования изменилось между версиями стандарта C ++ 03 и C ++ 11. В более старых версиях он был
operator void*() const;
(обычно реализован какreturn fail() ? NULL : this;
), а в более новыхexplicit operator bool() const;
(как правило, реализован какreturn !fail();
). Оба объявления отлично работают в логическом контексте, но ведут себя по-разному при (неправильном) использовании вне такого контекста.В частности, согласно правилам C ++ 03,
cout << cout
будет интерпретироватьсяcout << cout.operator void*()
и печатать какой-то адрес. Согласно правилам C ++ 11,cout << cout
не должен компилироваться вообще, поскольку оператор объявленexplicit
и, следовательно, не может участвовать в неявных преобразованиях. Фактически, это было основной причиной изменения - предотвращение компиляции бессмысленного кода. Компилятор, соответствующий любому стандарту, не создаст программу, которая печатает"1"
.По-видимому, некоторые реализации C ++ позволяют смешивать и сопоставлять компилятор и библиотеку таким образом, чтобы получить несоответствующий результат (цитируя @StephanLechner: «Я нашел параметр в xcode, который дает 1, и другой параметр, который дает адрес: Диалект языка c ++ 98 в сочетании с «Стандартная библиотека libc ++ (стандартная библиотека LLVM с поддержкой c ++ 11)» дает 1, тогда как c ++ 98 в сочетании с libstdc (стандартная библиотека GNU C ++) дает адрес; »). У вас может быть компилятор в стиле C ++ 03, который не понимает
explicit
операторов преобразования (которые появились в C ++ 11), в сочетании с библиотекой в стиле C ++ 11, которая определяет преобразование какoperator bool()
. С таким миксом становится возможнымcout << cout
интерпретировать какcout << cout.operator bool()
, что, в свою очередь, простоcout << true
и печатает"1"
.источник
Как говорит Игорь, вы получите это с C ++ 11 библиотеки, где
std::basic_ios
имеют теoperator bool
вместоoperator void*
, но как - то не объявлены (или рассматриваться как)explicit
. Правильную декларацию см. Здесь .Например, соответствующий компилятор C ++ 11 даст тот же результат с
но в вашем случае
static_cast<bool>
это (ошибочно) разрешено как неявное преобразование.Изменить: поскольку это необычное или ожидаемое поведение, может быть полезно знать вашу платформу, версию компилятора и т. Д.
Изменить 2: для справки код обычно записывается как
или как
и это смешивание двух стилей вместе выявило ошибку.
источник
Причина неожиданного вывода - опечатка. Вы, наверное, имели в виду
Если мы проигнорируем строки, которые имеют ожидаемый результат, мы останемся с:
Начиная с C ++ 11, это неправильно.
std::cout
не может быть неявно преобразован во что-либо, чтоstd::basic_ostream<char>::operator<<
(или перегрузка, не являющаяся членом) могла бы принять. Поэтому соответствующий стандартам компилятор должен, по крайней мере, предупредить вас об этом. Мой компилятор отказался компилировать вашу программу.std::cout
будет преобразовываться вbool
, а перегрузка bool оператора потокового ввода будет иметь наблюдаемый результат 1. Однако эта перегрузка является явной, поэтому она не должна допускать неявное преобразование. Похоже, что ваша реализация компилятора / стандартной библиотеки не полностью соответствует стандарту.В стандарте до C ++ 11 это хорошо сформировано. Тогда
std::cout
был оператор неявного преобразования, вvoid*
который была перегрузка оператора потокового ввода. Однако результат для этого будет другим. он напечатал бы адрес памятиstd::cout
объекта.источник
Опубликованный код не должен компилироваться для любого C ++ 11 (или более позднего совместимого компилятора), но он должен компилироваться даже без предупреждения для реализаций до C ++ 11.
Разница в том, что C ++ 11 сделал преобразование потока в bool явным:
ostream operator << определяется с помощью параметра bool. Поскольку преобразование в bool существовало (и не было явным) было до C ++ 11,
cout << cout
было переведено наcout << true
которое дает 1.И согласно C.2.15, это больше не должно компилироваться, начиная с C ++ 11.
источник
bool
в C ++ 03 не существовало, однако есть такое,std::basic_ios::operator void*()
которое имеет смысл в качестве управляющего выражения условия или цикла.Таким образом можно легко отладить свой код. Когда вы используете,
cout
ваш вывод буферизируется, поэтому вы можете анализировать его следующим образом:Представьте себе, что первое появление
cout
представляет буфер, а оператор<<
представляет добавление в конец буфера. Результатом оператора<<
в вашем случае является выходной потокcout
. Вы начинаете с:cout << "2+3 = " << cout << 2 + 3 << endl;
После применения вышеуказанных правил вы получите такой набор действий:
buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);
Как я сказал ранее, результатом
buffer.append()
является буфер. В начале ваш буфер пуст, и вам нужно обработать следующий оператор:заявление:
buffer.append("2+3 = ").append(cout).append(2 + 3).append(endl);
буфер: empty
Сначала у вас есть,
buffer.append("2+3 = ")
который помещает данную строку прямо в буфер и становитсяbuffer
. Теперь ваше состояние выглядит так:заявление:
buffer.append(cout).append(2 + 3).append(endl);
буфер: 2+3 =
После этого вы продолжаете анализировать свой оператор и сталкиваетесь
cout
с аргументом, который нужно добавить в конец буфера. Этоcout
обрабатывается1
так, что вы добавите1
в конце вашего буфера. Теперь вы в таком состоянии:заявление:
buffer.append(2 + 3).append(endl);
буфер: 2+3 = 1
Следующее, что у вас есть в буфере,
2 + 3
и поскольку сложение имеет более высокий приоритет, чем оператор вывода, вы сначала добавите эти два числа, а затем поместите результат в буфер. После этого вы получите:заявление:
buffer.append(endl);
буфер: 2+3 = 15
Наконец, вы добавляете значение
endl
в конец буфера, и у вас есть:заявление:
буфер: 2+3 = 15\n
После этого процесса символы из буфера выводятся из буфера на стандартный вывод один за другим. Итак, результат вашего кода
2+3 = 15
. Если вы посмотрите на это, вы получите дополнительную информацию1
от того,cout
что пытались напечатать. Удалив<< cout
из своего оператора, вы получите желаемый результат.источник
cout << cout
производят1
?» , и вы только что заявили, что это так, в середине обсуждения цепочки операторов вставки.