reinterpret_cast模板类从非const到const版本

时间:2018-03-26 11:57:24

标签: c++ templates type-conversion const

我有模板类ImageView<Pixel>,它存储一个指向数据和图像大小的非拥有指针。

我希望保持正确性,因此我与Pixelconst 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

明显的解决方案是使用构造函数添加implict转换:

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);
}

它似乎有用,但我不确定最后一个片段的正确性。

整个类的简化(仅省略了一些额外的非虚函数)版本:

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
};

3 个答案:

答案 0 :(得分:4)

是的,reinterpret_cast无效,您不能将对象强制转换为其他不相关类型的对象。嗯,你可以,但请不要随便访问它。

你可以添加一个转换运算符,而不是禁用隐式构造函数,因为你在非重载解析上下文中使用了SFINAE,因此无法工作(有一些解决方法,比如使条件相关,这将实现相同的目标)。但使用转换运算符更清晰IMO:

operator ImageView<const Pixel>() { return {m_data, m_width, m_height, m_stride}; }

你不必担心副本,编译器很聪明! :)而24字节真的没什么可担心的。

自己查看程序集here。 gcc在-O1及更高版本上生成相同的代码,用于将ImageView<const char>ImageView<char>传递给foo,以及用于-O2以上的clang。因此,如果使用优化进行编译,则完全没有区别。

答案 1 :(得分:1)

即使Public lastCBclicked as String Private Sub c1_Click() lastCBclicked = ActiveControl.Name End Sub Private Sub c2_Click() lastCBclicked = ActiveControl.Name End Sub Private Sub Commande6_Click() Select Case lastCBclicked Case "c1" DoCmd.OpenForm "FORM1" Case "c2" DoCmd.OpenForm "FORM2" Case Else 'traitement End Select End Sub 可隐式转换为const charcharImageView<char>是完全不相关的类型,我在这里也一无所知。但遗憾的是,从某种意义上说,ImageView<const char> ImageView<const char>,可以对其进行修改。

幸运的是,我们有一个工具来为编译器提供一些东西的东西。这是继承的定义(根据Liskov规则)。就是这样。从ImageView<char>开始ImageView<const char> 继承可以解决您的大多数问题,这是有道理的:

ImageView<char>

DEMO

答案 2 :(得分:0)

确实可以使用@YCS's idea来实现所需的行为,但它需要更复杂的代码和const_cast才能使用m_dataconst_cast这里是安全的,因为非const ImageView的构造函数只接受指向非const数据的指针。

所以现在,我将使用转换构造函数或运算符保留版本。如果我注意到临时工对性能的重大影响,我将回到这段代码:

template <class Pixel>
struct ImageView : public ImageView<const Pixel>
{
    constexpr ImageView(Pixel * data) noexcept
        : ImageView(data)
    {}

    constexpr Pixel * GetData() const noexcept
    {
        const Pixel * data = ImageView<const Pixel>::GetData();
        return const_cast<Pixel*>(data);
    }
};

template <class Pixel>
struct ImageView<const Pixel>
{
    constexpr ImageView(const Pixel * data) noexcept
        : m_data(data)
    {}

    constexpr const Pixel * GetData() const noexcept
    {
        return m_data;
    }

private:
    const Pixel * m_data;
};

int main()
{
    int * data = nullptr;
    const int * cdata = nullptr;

    ImageView<int> img(data);
    //ImageView<int> img1(cdata); // compile error
    ImageView<const int> cimg(data);
    ImageView<const int> cimg1(cdata);

    auto img2 = img;
    auto cimg2 = cimg;

    ImageView<const int> cimg3(img);
    ImageView<const int> cimg4 = static_cast<ImageView<const int>>(img);
    ImageView<const int> cimg5 = img;

    img.GetData();
    cimg.GetData();

    return 0;
}