Преобразование типов в определяемом пользователем классе в C++

Я создал матричный класс на C++.

namespace LinAlg {
template <typename T> class Matrix {
public:
  Matrix();
  Matrix(const uint64_t &rows, const uint64_t &cols);
  template <typename P> operator P() const;

private:
  uint64_t rows_, cols_;
  std::vector<std::vector<T>> arr_;
 
};

template <typename T> template <typename P> Matrix<T>::operator P() const {
  if constexpr (std::is_same_v<P, Matrix<int>> ||
                std::is_same_v<P, Matrix<double>> ||
                std::is_same_v<P, Matrix<float>>) {
    Matrix<P> m(this->rows_, this->cols_);
    for (uint64_t i = 0; i < this->rows_; i++) {
      for (uint64_t j = 0; j < this->cols_; j++) {
        m.arr_[i][j] = static_cast<P>(this->arr_[i][j]);
      }
    }
    return m;
  }
  throw std::invalid_argument("Not a valid type conversions\n");
}
} // namespace LinAlg

Я реализовал преобразование типов Matrix<T> в Matrix<P>. Когда я попытался преобразовать Matrix<int> в Matrix<float> в своем test.cpp, используя следующий код:

  LinAlg::Matrix<int> M(3, 3);
  std::cin >> M;
  LinAlg::Matrix<float> M1(3, 3);
  std::cin >> M1;
  std::cout << static_cast<LinAlg::Matrix<float>>(M) + M1 << "\n";

Я получаю ошибки типа arr_ — это private в контексте:

m.arr_[i][j] = static_cast<P>(this->arr_[i][j]);
Matrix<A> и Matrix<B> — разные и неродственные типы. Частное Matrix<A>::arr_ не будет доступно в Matrix<B>. Вы могли бы сделать их друзьями.
Evg 07.07.2024 18:34

У вашего Matrix, вероятно, есть какие-то общедоступные методы для получения/установки значений в матрице. Вы можете использовать его для решения проблемы, упомянутой в комментарии выше.

wohlstad 07.07.2024 18:40

@wohlstad Я мог бы реализовать метод установки, но тогда какой смысл объявлять членов частными, если пользователь может манипулировать ими с помощью установки?

Vedant Yadav 07.07.2024 18:56

Инкапсуляция частных членов не противоречит использованию сеттеров. Но в любом случае, как вы предполагали, что пользователи класса будут устанавливать данные? (Кстати, вы также не публиковали никаких геттеров, поэтому тот же вопрос актуален для любого доступа).

wohlstad 07.07.2024 18:57

Другая проблема: в m.arr_[i][j] = static_cast<P>(this->arr_[i][j]); - поскольку P - это вся матрица (а не тип элемента) - static_cast<P> попытка применить ее к элементу неверна.

wohlstad 07.07.2024 19:05

... и у вас похожая проблема с Matrix<P> m(...);.

wohlstad 07.07.2024 19:10

@VedantYadav опубликовал полный ответ на все проблемы, упомянутые выше.

wohlstad 07.07.2024 19:22
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать 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
7
84
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Ваша главная проблема в том, что Matrix<T1> и Matrix<T2> (для типов T1,T2) — разные и не связанные между собой типы.

Поэтому Matrix<T1> не может получить доступ к частному члену в Matrix<T2>, и это является источником ошибки, которую вы получаете о том, что arr_ является частным в этой строке:

m.arr_[i][j] = static_cast<P>(this->arr_[i][j]);

Преодолеть эту проблему можно двумя способами:

  1. Добавьте общедоступные методы доступа (сеттеры, геттеры) в Matrix и используйте их в реализации operator P() вместо доступа к частному arr_.
    Мне это кажется лучшим решением, потому что пользователям класса, вероятно, в любом случае потребуются методы доступа для доступа к данным.

  2. Если у вас есть веские причины избегать добавления аксессоров, и поскольку вы, похоже, поддерживаете только Matrix<int>,Matrix<double>, Matrix<float> (на основании вашего if constexpt), то вы можете подружиться с этими 3 классами:

    friend Matrix<int>;
    friend Matrix<double>;
    friend Matrix<float>;
    

    Это позволит вам получить доступ к приватному arr_.


Другая проблема в вашем коде заключается в том, что P — это тип целого Matrix<X> (для некоторого типа X), а не тип элемента в Matrix.
Поэтому эти две строки неверны:

Matrix<P> m(this->rows_, this->cols_);

И:

m.arr_[i][j] = static_cast<P>(this->arr_[i][j]);

Потому что они оба предполагают, что P — это тип элементов в Matrix.

Чтобы решить эту проблему, вы можете добавить псевдоним типа в Matrix (может быть полезно сделать его общедоступным):

template <typename T> class Matrix {
public:
    using element_type = T;

    // ...
};

Затем используйте его в двух строках выше:

Matrix<P::element_type> m(...);

И:

... = static_cast<P::element_type>(...);

Примечание. Возможно, вам придется использовать typename P::element_type вместо P::element_type (если ваша версия C++ более ранняя, чем C++20).

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