правильность типов доступа к данным - лучшее решение?

0

Я работаю над классом изображений, который позволяет работать с изображениями с разной компоновкой пикселей (RGB, RGBA, Gray, Bayer,...). Для доступа к пикселю можно вызвать image.at<PixelType>(x,y) который возвращает "Accessor". Конкретная реализация Accessor зависит от аргумента шаблона. Но теперь я столкнулся с некоторыми проблемами относительно корректности const.

Вот очень неуклюжая реализация, которая, надеюсь, делает это очевидным:

template<bool constAccessor>
class Accessor {
public:
    typedef typename boost::mpl::if_c<constAccessor, const int, int>::type DataType;

    Accessor(DataType& data) 
    :data(data) {
    }

    Accessor(Accessor<false>& other) 
    : data(other.data) {
    }

    DataType& data;
};


class Image {
public:
    Accessor<false> at(unsigned int x, unsigned int y) {
        return Accessor<false>(data);
    }
    Accessor<true> at(unsigned int x, unsigned int y) const {
        return Accessor<true>(data);
    }

private:
    int data;
};

int main() {
    Image img;
    const Image& cimg = img;

    // get accessor which is non-const
    Accessor<false> a1 = img.at(0, 0); 

    // get a accessor which is const...
    Accessor<true> a2 = a1;
    // ... modifying a value results in an error
    a2.data = 42;

    // try to convert a accessor which is const to a non-const version
    // ... results in an error
    Accessor<false> a3 = a2;

    return 0;
}

Как вы можете видеть, существует неконстантная и константная реализация метода at. В зависимости от константы аргумент шаблона для accessor устанавливается в true или false. Но теперь у меня есть два разных типа (const и non-const) для типа accessor/pixel, из-за чего необходимо написать конструктор преобразования, потому что в противном случае тестовые примеры, показанные в функции main(), не будут работать.

Теперь вопрос: есть ли лучший способ достичь этого? Мне кажется, что плохо использовать аргумент шаблона в качестве индикатора константы. Было бы гораздо приятнее использовать Accessor и const Accessor. С другой стороны, это похоже на то, что делает std-библиотека с ::iterator и ::const_iterator. У кого-нибудь есть опыт в таких ситуациях?

  • 0
    Вы можете сделать что-то похожее на то, что стандартные контейнеры делают с итераторами, в том, что у него есть два разных итератора: iterator и const_iterator , заставляя программиста думать о том, какой итератор использовать. К сожалению, программисты редко думают об этом и используют обычный iterator даже когда const_iterator будет лучшим.
  • 0
    @JoachimPileborg Я попытался уточнить, что я думаю, что мой подход похож на подход из библиотеки std. Вопрос в том, есть ли лучшее решение.
Теги:
templates
const

1 ответ

2
Лучший ответ

Вы можете (over-) обобщить ваш параметр от наличия двух булевых состояний для любого типа значения:

template<typename Value>
class Accessor {
public:    
    Accessor(Value& data) 
        : data(data)
    {}

    template<typename T, EnableIf<std::is_convertible<T&, Value&>>...>
    Accessor(Accessor<T> const& other)
        : data(other.data)
    {}

    Value& data;
};

Очевидно, это не отличается от того, что у вас есть, кроме как под другим видом: вместо Accessor<false> и Accessor<true> вас есть Accessor<DataType> и Accessor<DataType const>.

Преимущество - знакомство: все, например, std::unique_ptr<T>, std::shared_ptr<T>, std::reference_wrapper<T> (и даже T*) ведут себя одинаково. В частности, эта famialiarity, вероятно, должна распространяться на ошибки компилятора, связанные с отсутствием преобразования из Accessor<DataType const> в Accessor<DataType>, так же, как вы не можете преобразовать из int const* в int*.

  • 0
    Спасибо за ваш ответ - особенно второй абзац прояснил, что это поведение не так уж и нетипично

Ещё вопросы

Сообщество Overcoder
Наверх
Меню