I'm working on a image-class which makes it possible to work with images with different pixel layouts (RGB, RGBA, Gray, Bayer, ...). To access a pixel it's possible to call image.at<PixelType>(x,y)
which returns an "Accessor". The concrete Accessor-implementation is dependent on the template argument. But now i've ran into some issues regarding const correctness.
Here is a very dumbed down implementation which hopefully makes it apparent:
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;
}
As you can see there is a non-const and a const implementation of the at
method. Depending on the constness the template argument of the accessor is set to true
or false
. But now i've got two different types (const and non-const) per accessor/pixel type which makes it necessary to write a conversion constructor because otherwise the test-cases shown in the main()
function wouldn't work.
Now the question is: Is there a better way to achieve this? It feels kind of bad to use a template argument as const indicator. It would be much nicer to just use Accessor
and const Accessor
. On the other hand this is similar to what the std-library does with ::iterator
and ::const_iterator
. Anyone has some experience with such situations?
You can (over-)generalise your parameter from having the two boolean states to any value type instead:
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;
};
Obviously this isn't any different than what you have, except under another guise: instead of Accessor<false>
and Accessor<true>
, you have Accessor<DataType>
and Accessor<DataType const>
.
The benefit is familiarity: all of eg std::unique_ptr<T>
, std::shared_ptr<T>
, std::reference_wrapper<T>
(and even T*
) behave the same way. In particular, this famialiarity should hopefully extend to compiler errors about a lack of conversion from Accessor<DataType const>
to Accessor<DataType>
, much like you can't convert from int const*
to int*
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.