Как реализовать несколько операторов вставки (<<) для класса C++?

Я вывожу объекты своего класса, используя cout или ofstream. Я понял, что оператор вставки << — это стандартный способ преобразования объекта в строку или его печати. Я хотел бы использовать разные форматы в разных частях проекта.

Я хотел бы иметь для класса много разных форматов строк. Однако для класса может быть только один оператор вставки.

Каковы распространенные способы решения этой проблемы в C++?

Одно из решений, которое я использовал, — это иметь несколько функций, возвращающих строки. Насколько я понимаю, перегрузка оператора вставки является наиболее распространенным способом вывода объектов, и мне бы хотелось иметь такое решение.

Использование разных форматов звучит как образец стратегии. Я думаю, что шаблон также является мощным инструментом для решения многих проблем. Я не знаком ни с одним из вариантов решения.

Вот пример кода, демонстрирующий проблему:

#include <iostream>
#include <sstream>

class Point {
  public:
    Point(int x, int y): x(x), y(y) {}

    std::string to_string() {
      std::ostringstream stream;
      stream << "(" << x << ", " << y << ")";
      return stream.str();
    }   

    friend std::ostream& operator<<(std::ostream& out, const Point& p) {
      return out << p.x << " " << p.y;
    }   

  private:
    int x, y;
};

int main() {
  Point p { 4, 2 };
  std::cout << p << std::endl;
  std::cout << p.to_string() << std::endl;
}

Я хотел бы избавиться от функции to_string и использовать операторы вставки для получения всех форматов. Каков простой способ определить, какой оператор вставки формата используется. Должна быть возможность изменить формат объекта.

Класс может иметь более двух разных форматов.

ваше решение в порядке. Конечно, есть разные способы, но если вы не хотите to_string, чего еще вы хотите? std::cout << p; для одного формата, а что для другого?

463035818_is_not_an_ai 23.05.2024 21:07

Если вы можете использовать C++20, есть std::format, который позволяет вам иметь printf выходные строки, но с безопасностью типов. std::formatter можно специализировать для вашего типа, чтобы он работал с ним std::format

NathanOliver 23.05.2024 21:14

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

NathanOliver 23.05.2024 21:15

Вы можете создать свой собственный манипулятор потока ввода-вывода и иметь поведение операторов потока своих собственных классов в зависимости от состояния манипулятора вашего клиента, которое вы ввели в поток. qv. пользовательские манипуляторы потоков C++. Однажды я пошел по этому пути и пришел к выводу, что это не решение моей проблемы (ввод-вывод одним из четырех способов: двоичный для файла, текстовый для файла, текстовый для пользователя, текстовый для -отладка). Но, возможно, это решение, подходящее для вашей проблемы.

Eljay 23.05.2024 21:19
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
74
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Для этой задачи вы можете определить свои собственные манипуляторы потоков ввода-вывода. Вы также можете:

  • оберните объект Point другим типом структуры/класса, для которого вы перегружаете operator<<, например:
#include <iostream>

class Point {
  public:
    Point(int x, int y): x(x), y(y) {}
  private:
    int x, y;
    friend struct s_Point_printer;
};

struct s_Point_printer {
    const Point &m_pt;
    const bool m_parenthesis;

    s_Point_printer(const Point &pt, bool parenthesis) : m_pt(pt), m_parenthesis(parenthesis) {}

    void printTo(std::ostream &out) const {
        if (m_parenthesis)
            out << "(" << m_pt.x << ", " << m_pt.y << ")";
        else
            out << m_pt.x << " " << m_pt.y;
    }

    friend std::ostream& operator<<(std::ostream &out, const s_Point_printer &printer) {
        printer.printTo(out);
        return out;
    }
};

s_Point_printer with_parens( const Point& pt ) {
    return s_Point_printer{pt, true};
}

s_Point_printer without_parens( const Point& pt ) {
    return s_Point_printer{pt, false};
}

int main() {
  Point p { 4, 2 };
  std::cout << without_parens(p) << std::endl; // prints "4 2"
  std::cout << with_parens(p) << std::endl;    // prints "(4, 2)"
}

Онлайн-демо

  • Альтернативно, вы можете сохранить желаемый параметр форматирования непосредственно внутри самого std::ostream, а затем перегрузить operator<< для Point, проверив этот параметр, например:
class Point {
  public:
    static int point_xalloc;

    Point(int x, int y): x(x), y(y) {}

    friend std::ostream& operator<<(std::ostream& os, const Point& pt) {
        if (os.iword(Point::point_xalloc) == 1)
            return os << "(" << pt.x << ", " << pt.y << ")";
        else
            return os << pt.x << " " << pt.y;
    }

  private:
    int x, y;
};
 
int Point::point_xalloc = std::ios_base::xalloc();

std::ios_base& with_parens(std::ios_base& os) {
    os.iword(Point::point_xalloc) = 1;
    return os;
}
 
std::ios_base& without_parens(std::ios_base& os) {
    os.iword(Point::point_xalloc) = 0;
    return os;
}

int main() {
  Point p { 4, 2 };
  std::cout << without_parens << p << std::endl; // prints "4 2"
  std::cout << with_parens << p << std::endl;    // prints "(4, 2)"
}

Онлайн-демо

Другие вопросы по теме