Функция, которая печатает что-то в std::ostream и возвращает std::ostream?

Я хочу написать функцию, которая выводит что-то в 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.

7 ответов


вы не можете исправить функцию. Ничто в спецификации не требует, чтобы компилятор оценивал вызов функции в выражении в любом определенном порядке относительно некоторого несвязанного оператора в том же выражении. Поэтому, не меняя код вызова, вы не можете сделать MyPrint() оценить после std::cout << "Value: "

порядок слева направо является обязательным для выражений, состоящих из нескольких последовательных операторов

вы не можете достичь того же самого с бесплатными вызовами функций, потому что у них нет 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 классом с оператором friend

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

нет способа сделать то, что вы ожидаете, из-за порядка, в котором оцениваются функции.

есть ли какая-то особая причина, по которой вам нужно писать прямо в поток? Если нет, просто попросите 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;

если этого недостаточно, объясните, какой вывод вы хотите увидеть.