Есть ли лучший способ реализовать константную версию функции-члена row() без использования const_cast?

Пример взят из демонстрации использования std::slice и std::slice_array:

https://en.cppreference.com/w/cpp/numeric/valarray/slice

https://en.cppreference.com/w/cpp/numeric/valarray/slice_array

Как правильно определить константную версию функции-члена row()? Следующий код компилируется, но использует const_cast. Я был бы очень признателен, если бы вы могли дать какое-либо предложение.

#include <iostream>
#include <valarray>
class Matrix {
    std::valarray<int> data;
    int dim;
 public:
    Matrix(int r, int c) : data(r*c), dim(c) {}
    int& operator()(int r, int c) {return data[r*dim + c];}
    std::slice_array<int> row(std::size_t row) {
        return data[std::slice(dim*row, dim, 1)];
    }
    const std::slice_array<int> row(std::size_t row) const {
        return const_cast<std::valarray<int>&>(data)[std::slice(dim*row, dim, 1)];
    }
};
int main()
{
    Matrix m(3,3);
    int n = 0;
    for(int r=0; r<3; ++r)
       for(int c=0; c<3; ++c)
           m(r, c) = ++n;
    
    const Matrix m2(m);

    for(int r=0; r<3; ++r) {
        for(int c=0; c<3; ++c) {
            std::cout << m(r, c) << " ";
        }
        std::cout << std::endl;
    }

    // non-const
    std::slice_array<int> isa = m.row(0);
    std::valarray<int> iva(isa);
    for (auto elem : iva) { std::cout << elem << std::endl;} 
    
    // const
    const std::slice_array<int> isa2 = m2.row(0);
    const std::valarray<int> iva2(isa2);
    for (auto elem : iva) { std::cout << elem << std::endl;} 

    return 0;
}

Если функция-член определена следующим образом:

    const std::slice_array<int> row(std::size_t row) const {
        return data[std::slice(dim*row, dim, 1)];
    }

то выдается следующее сообщение об ошибке:

valarray_demo.cpp:13:16: error: no viable conversion from returned value of type '__val_expr<__slice_expr<const std::__1::valarray<int> &> >' to function return type 'const std::slice_array<int>'
        return data[std::slice(dim*row, dim, 1)];
               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../include/c++/v1/valarray:1149:28: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from
      '__val_expr<__slice_expr<const std::__1::valarray<int> &> >' to 'const std::__1::slice_array<int> &' for 1st argument
class _LIBCPP_TEMPLATE_VIS slice_array

тест на: macOS Catalina; Apple clang версии 11.0.3 (clang-1103.0.32.29)


person ypan1988    schedule 07.02.2021    source источник


Ответы (1)


Обратите внимание, что в типе возвращаемого значения const std::slice_array<int> квалификатор верхнего уровня const игнорируется компилятором, поэтому фактический тип возвращаемого значения — std::slice_array<int>. То же правило применяется к функции параметры.

Вероятно, вам нужен срез для постоянных элементов, а не постоянный срез для изменяемых элементов.


std::valarray не предлагает срезы для постоянных элементов:

std::slice_array<T> operator[](std::slice slicearr);
std::valarray<T>    operator[](std::slice slicearr) const;

Версия const возвращает копию элементов и, следовательно, вообще не может быть реализована для возврата среза к постоянным элементам.


std::valarray — странный зверь, давно заброшенная попытка поддержки линейной алгебры в стандартной библиотеке C++. Для рабочего кода вы можете использовать библиотеку Eigen: Eigen — это библиотека шаблонов C++ для линейной алгебры. : матрицы, векторы, численные решатели и связанные алгоритмы.

person Maxim Egorushkin    schedule 07.02.2021