Функция, которая печатает что-то в std::ostream и возвращает std::ostream?
-
23-08-2019 - |
Вопрос
Я хочу написать функцию, которая выводит что-то в ostream
это передано, и вернуть поток, вот так:
std::ostream& MyPrint(int val, std::ostream* out) {
*out << val;
return *out;
}
int main(int argc, char** argv){
std::cout << "Value: " << MyPrint(12, &std::cout) << std::endl;
return 0;
}
Было бы удобно напечатать значение вот так и встроить вызов функции в цепочку операторов вывода, как я это сделал в main()
.
Однако он не работает и печатает это:
$ ./a.out
12Value: 0x6013a8
Желаемый результат будет таким:
Value: 12
Как я могу это исправить?Должен ли я определить operator<<
вместо?
ОБНОВЛЯТЬ: Уточнил, какой будет желаемый результат.
ОБНОВЛЕНИЕ2: Некоторые люди не понимали, почему я печатаю такое число, используя функцию вместо того, чтобы печатать его напрямую.Это упрощенный пример, и на самом деле функция печатает сложный объект, а не int
.
Решение
Вы не можете исправить функцию.Ничто в спецификации не требует, чтобы компилятор оценивал вызов функции в выражении в каком-либо определенном порядке относительно какого-либо несвязанного оператора в том же выражении.Таким образом, без изменения кода вызова вы не сможете сделать MyPrint()
оценить после std::cout << "Value: "
Порядок слева направо обязателен для выражений, состоящих из нескольких последовательных операторов <<, так что это будет работать.Смысл оператора<<, возвращающего поток, заключается в том, что когда операторы объединены в цепочку, LHS каждого из них предоставляется вычислением оператора слева от него.
Вы не можете добиться того же с помощью бесплатных вызовов функций, потому что у них нет LHS. MyPrint()
возвращает объект, равный std::cout
, и так же std::cout << "Value: "
, так что вы эффективно делаете std::cout << std::cout
, который печатает это шестнадцатеричное значение.
Поскольку желаемый результат:
Value: 12
«правильное» решение — это переопределить оператор<<.Зачастую это означает, что вам нужно либо сделать его другом, либо сделать следующее:
class WhateverItIsYouReallyWantToPrint {
public:
void print(ostream &out) const {
// do whatever
}
};
ostream &operator<<(ostream &out, const WhateverItIsYouReallyWantToPrint &obj) {
obj.print(out);
}
Если переопределить operator<<
для вашего класса не подходит, например, потому что существует несколько форматов, которые вы, возможно, захотите распечатать, и вы хотите написать отдельную функцию для каждого из них, тогда вам следует либо отказаться от идеи цепочки операторов и просто вызвать функцию или напишите несколько классов, которые принимают ваш объект в качестве параметра конструктора, каждый с разными перегрузками операторов.
Другие советы
Вы хотите сделать MyPrint классом с дружественным оператором<<:
class MyPrint
{
public:
MyPrint(int val) : val_(val) {}
friend std::ostream& operator<<(std::ostream& os, const MyPrint& mp)
{
os << mp.val_;
return os;
}
private:
int val_;
};
int main(int argc, char** argv)
{
std::cout << "Value: " << MyPrint(12) << std::endl;
return 0;
}
Этот метод требует, чтобы вы вставили объект MyPrint в выбранный вами поток.Если вам ДЕЙСТВИТЕЛЬНО нужна возможность изменить активный поток, вы можете сделать это:
class MyPrint
{
public:
MyPrint(int val, std::ostream& os) : val_(val), os_(os) {}
friend std::ostream& operator<<(std::ostream& dummy, const MyPrint& mp)
{
mp.os_ << mp.val_;
return os_;
}
private:
int val_;
std::ostream& os_
};
int main(int argc, char** argv)
{
std::cout << "Value: " << MyPrint(12, std::cout) << std::endl;
return 0;
}
У вас есть два варианта.Во-первых, используя то, что у вас уже есть:
std::cout << "Value: ";
MyPrint(12, &std::cout);
std::cout << std::endl;
Другой вариант, более похожий на C++, заключается в замене MyPrint()
с соответствующими std::ostream& operator<<
.уже есть один для int
, поэтому я сделаю немного сложнее:
#include <iostream>
struct X {
int y;
};
// I'm not bothering passing X as a reference, because it's a
// small object
std::ostream& operator<<(std::ostream& os, const X x)
{
return os << x.y;
}
int main()
{
X x;
x.y = 5;
std::cout << x << std::endl;
}
Невозможно сделать то, что вы ожидаете, из-за порядка, в котором оцениваются функции.
Есть ли какая-то особая причина, по которой вам нужно писать прямо в ostream?Если нет, просто попросите MyPrint вернуть строку.Если вы хотите использовать поток внутри MyPrint для генерации вывода, просто используйте strstream и верните результат.
Во-первых, нет причин не пройти ostream
по ссылке, а не по указателю:
std::ostream& MyPrint(int val, std::ostream& out) {
out << val;
return out;
}
Если вы действительно не хотите использовать std::ostream& operator<<(std::ostream& os, TYPE)
, вы можете сделать это:
int main(int argc, char** argv){
std::cout << "Value: ";
MyPrint(12, std::cout) << std::endl;
return 0;
}
После изменения указателя на ссылку вы можете сделать это:
#include <iostream>
std::ostream& MyPrint(int val, std::ostream& out) {
out << val;
return out;
}
int main(int, char**) {
MyPrint(11, std::cout << "Value: ") << std::endl;
return 0;
}
Синтаксис для MyPrint
по сути представляет собой развернутый operator<<
но с дополнительным аргументом.
В вашем случае ответ очевиден:
std::cout << "Value: " << 12 << std::endl;
Если этого недостаточно, объясните, какой результат вы хотите увидеть.