Função que imprime algo para std :: ostream e retorna std :: ostream?
-
23-08-2019 - |
Pergunta
Eu quero escrever uma função que saídas algo a um ostream
que é passado, e retornar o fluxo, como este:
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;
}
Seria conveniente para imprimir o valor como este e incorporar a chamada de função na cadeia operador de saída, como eu fiz no main()
.
Ele não funciona, no entanto, e imprime o seguinte:
$ ./a.out
12Value: 0x6013a8
A saída desejada seria esta:
Value: 12
Como posso corrigir isso? Eu tenho que definir uma operator<<
vez?
UPDATE:. Esclarecido que a saída desejada seria
UPDATE2: Algumas pessoas não entendem por que eu iria imprimir um número como esse, usando uma função em vez de imprimi-lo diretamente. Este é um exemplo simplificado, e, na realidade, a função imprime um objecto complexo em vez de um int
.
Solução
Você não pode consertar a função. Nada na especificação exige um compilador para avaliar uma chamada de função em uma expressão em qualquer ordem particular no que diz respeito a algum operador não ligado na mesma expressão. Então, sem alterar o código de chamada, você não pode fazer MyPrint()
avaliar após std::cout << "Value: "
ordem da esquerda para a direita está mandatado para expressões que consistem em vários << operadores consecutivos, de modo que vai funcionar. O ponto de operador << retornar o fluxo é que quando os operadores estão acorrentados, os LHS de cada um é fornecido pela avaliação do operador à sua esquerda.
Você não pode conseguir a mesma coisa com chamadas de função livres, porque eles não têm um LHS. MyPrint()
retorna um objeto igual a std::cout
, e assim faz std::cout << "Value: "
, então você está fazendo efetivamente std::cout << std::cout
, que é a impressão de que o valor hexadecimal.
Desde a saída desejada é:
Value: 12
a coisa "certa" a fazer é realmente para operador override <<. Isto significa frequentemente você precisa se quer torná-lo um amigo, ou fazer isso:
class WhateverItIsYouReallyWantToPrint {
public:
void print(ostream &out) const {
// do whatever
}
};
ostream &operator<<(ostream &out, const WhateverItIsYouReallyWantToPrint &obj) {
obj.print(out);
}
Se substituindo operator<<
para sua classe não é o caso, por exemplo, porque existem vários formatos que você pode querer imprimir, e você quer escrever uma função diferente para cada um, então você deve ou desistir da idéia de operador de encadeamento e apenas chamar a função, ou então escrever várias classes que tirar o objeto como um parâmetro de construtor, cada um com diferentes sobrecargas de operador.
Outras dicas
Você quer fazer MyPrint uma classe com o amigo operador <<:
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;
}
Este método requer que você inserir o objeto MyPrint na corrente de sua escolha. Se você precisa REALMENTE a capacidade de mudança que fluxo está ativa, você pode fazer isso:
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;
}
Você tem duas opções. O primeiro, usando o que você já tem é:
std::cout << "Value: ";
MyPrint(12, &std::cout);
std::cout << std::endl;
A outra, que é mais C ++ - como, é substituir MyPrint()
com o std::ostream& operator<<
apropriado. Já existe um para int
, por isso vou fazer um apenas um pouco mais complexo:
#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;
}
Não há nenhuma maneira de fazer o que você está esperando lá por causa da ordem das funções são avaliadas no.
Há alguma razão especial que você precisa para escrever diretamente para o ostream assim? Se não, só tem MyPrint retornar uma string. Se você quiser usar um fluxo dentro MyPrint para gerar a saída, basta usar um strstream e retornar o resultado.
Em primeiro lugar, não há nenhuma razão para não passar na ostream
por referência em vez de um ponteiro:
std::ostream& MyPrint(int val, std::ostream& out) {
out << val;
return out;
}
Se você realmente não quiser usar std::ostream& operator<<(std::ostream& os, TYPE)
, você pode fazer isso:
int main(int argc, char** argv){
std::cout << "Value: ";
MyPrint(12, std::cout) << std::endl;
return 0;
}
Depois de alterar o ponteiro para uma referência, você pode fazer isso:
#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;
}
A sintaxe para MyPrint
é essencialmente a de um operator<<
desenrolado mas com um argumento extra.
No seu caso, a resposta é, obviamente:
std::cout << "Value: " << 12 << std::endl;
Se isso não é bom o suficiente, por favor explicar o que de saída que você quer ver.