【发布时间】:2018-03-26 11:57:23
【问题描述】:
我有模板类ImageView<Pixel>,它存储了一个指向数据和图像大小的非拥有指针。
我想要 const 正确性,所以我同时使用 Pixel 和 const Pixel:
std::byte * data;
ImageView<char> img(data, width, height);
std::byte const* cdata;
ImageView<char> img(cdata, width, height); // compile error
ImageView<const char> cimg(cdata, width, height);
但是这样的代码当然会导致问题:
void foo(ImageView<const char> const&);
ImageView<char> view;
foo(view); // conversion from ImageView<char> to ImageView<const char> const& required
明显的解决方案是使用构造函数添加隐式转换:
template <class = std::enable_if_t<std::is_const_v<Pixel>>>
constexpr ImageView(ImageView<std::remove_const_t<Pixel>> const& other) noexcept
: m_data(other.GetData())
, m_stride(other.GetStride())
, m_width(other.GetWidth())
, m_height(other.GetHeight())
{}
但它的缺点是在每次转换时都会创建临时文件,ImageView 在大多数 64 位平台上是 24 字节。这个临时文件与原始文件的不同之处仅在于类型——它们的布局完全相同。于是我开始考虑使用reinterpret_cast和const引用转换运算符:
template <class = std::enable_if_t<!std::is_const_v<Pixel>>>
constexpr operator ImageView<std::add_const_t<Pixel>> const&() const noexcept
{
using ConstImageView = ImageView<std::add_const_t<Pixel>>;
return *reinterpret_cast<ConstImageView const*>(this);
}
似乎可行,但我不确定最后一个 sn-p 的正确性。
整个类的简化版(仅省略了一些额外的非虚函数):
template <class Pixel>
class ImageView
{
template <class T, class U>
using copy_const_qualifier =
std::conditional_t<
std::is_const_v<T>,
std::add_const_t<U>,
std::remove_const_t<U>>;
using Byte = copy_const_qualifier<Pixel, std::byte>;
public:
constexpr ImageView(Byte * data, unsigned w, unsigned h, std::size_t s) noexcept
: m_data(data)
, m_stride(s)
, m_width(w)
, m_height(h)
{}
constexpr Byte * GetData() const noexcept { return m_data; }
constexpr std::size_t GetStride() const noexcept { return m_stride; }
constexpr unsigned GetWidth() const noexcept { return m_width; }
constexpr unsigned GetHeight() const noexcept { return m_height; }
protected:
Byte * m_data;
std::size_t m_stride; // in bytes
unsigned m_width; // in pixels
unsigned m_height; // in pixels
};
【问题讨论】:
-
为什么要担心 24byte。过早的优化是根本...
标签: c++ templates type-conversion constants