Повторно использовать оператор << для нескольких классов - PullRequest
2 голосов
/ 16 февраля 2020

У меня есть два класса (это извлечение из моего более сложного случая)

class ClUInt {
public:
    ClUInt() : _i(2), _arr{1,2,3} {};
    ClUInt(const unsigned int ui) : _i(ui) {};
private:
    unsigned int _i;
    double _arr[3];
};

class ClInt {
public:
    ClInt() : _i(-2), _arr{-1,-2,-3} {};
    ClInt(const int i) : ClInt() { _i = i; };
private:
    int _i;
    double _arr[3];
};

Они очень похожи, но один использует int, а другой unsigned int для члена _i.

Я хочу перегрузить operator<<, например,

std::ostream& operator<<(std::ostream& os, const ClInt & ci)
{
    cout << ci._i << endl;
    cout << ci._arr[0] << endl;
    return os;
}

Предположим, я хочу "одинаковую" перегрузку для обоих классов.

Как Могу ли я написать его только один раз, чтобы его было проще поддерживать? Я думал о том, чтобы определить свой собственный состав, но я не уверен, что это способ go ...

Примечания:

  1. Я думаю У меня нет шансов заставить два класса разделить какую-либо часть дерева наследования.

  2. Они могли бы на самом деле struct с, поэтому, если конфиденциальность влияет на ответ, можно предположить, что _i и _arr равны public.

  3. В данном случае два struct s имеют большее количество «общих» членов, которые подписаны / не подписаны, соответственно.

Ответы [ 3 ]

3 голосов
/ 16 февраля 2020

Используйте шаблон с понятиями:

#include <concepts>
#include <iostream>

template<typename T>
concept ClInteger = std::same_as<T, ClInt> || std::same_as<T, ClUInt>;

template <ClInteger T>
std::ostream& operator<<(std::ostream& os, const T & ci)
{
    std::cout << ci._i << '\n';
    std::cout << ci._arr[0] << '\n';
    return os;
}

Обратите внимание, что этот оператор должен быть другом этих классов, чтобы иметь доступ к их личным полям.
LIVE

2 голосов
/ 16 февраля 2020

Вы можете попробовать создать шаблон operator<<, который работает только для этих двух типов, используя SFINAE. Например:

template <class T,
  std::enable_if_t<
      std::is_same<ClInt, std::decay_t<T>>::value
      or std::is_same<ClUInt, std::decay_t<T>>::value
    , int> = 0>
std::ostream & operator<< (std::ostream & out, T const & obj) {
  out << obj._i << '\n';
  out << obj._arr[0] << '\n';
  return out;
}

Приведенный выше пример предназначен для C ++ 14, но его можно использовать для работы с C ++ 11, если заменить такие вещи, как std::decay_t<T> на typename std::decay<T>::type.

* 1008. * Примечание: я заменил std::endl на '\n', так как вы, вероятно, не хотите каждый раз очищать поток вывода. См. C ++: "std :: endl" против "\ n" .
0 голосов
/ 16 февраля 2020

Может быть, это просто совпадение, но ваши два класса имеют много общего. Итак, почему бы не превратить их в шаблоны:

template<typename integer_type>
class IntegerClass
{
    /// ... further code elided

    friend ostream& operator<<(ostream& out, IntegerClass const& obj)
    {
        return out
            << obj._i << endl
            << obj._arr[0] << endl;
    }
    integer_type _i;
    double _arr[3];
};

// optional typedefs
// These are a matter of taste, not actually mine.
typedef IntegerClass<unsigned int> ClUInt;
typedef IntegerClass<signed int> ClInt;
...