c++ - 如何使用运算符编写可继承的模板类

标签 c++ templates inheritance operator-overloading

我希望可以编写一个模板类,该类将被多个特定类型的子类继承。我希望继承的方法和运算符返回子类的类型而不是父模板类型。如果我只需要修改一个基类,这是希望节省大量的开发和维护工作。

这是我已有的示例:

template<typename T> struct TMonoPixel
{
    T value;

    TMonoPixel(T v) { value = v; }

    // the template has some pure virtual functions here...

    TMonoPixel operator+ (const TMonoPixel& other)
    { return TMonoPixel(value + other.value); }
}

struct Mono8Pixel : TMonoPixel<uint8_t>
{
    using TMonoPixel::TMonoPixel;    // I want to inherit the constructor
    // each pixel type implements the virtual functions in the template
}

如您所见,Mono8Pixel 结构继承了 +接受 TMonoPixel 的运算符,但使用此运算符返回 TMonoPixel<uint8_t>而不是 Mono8Pixel因为它是在基类中定义的。

我计划使用这些结构来迭代图像中的像素:

Image* img; // img has an unsigned char* pointer to its pixel data
for (int row=0; row<img->height; row++) {
    for (int col=0; col<img->width; col++) {
        int i = (row*img->width + col);
        Mono8Pixel* pixel = reinterpret_cast<Mono8Pixel*>(img->dataPtr + sizeof(unsigned char)*i);
        // modify the pixel ...
    }
}

有没有办法只改变模板类来确保Mono8Pixel(2) + Mono8Pixel(2)正在返回 Mono8Pixel

请注意,无论解决方案是什么,这些结构都必须保持标准布局,因为我希望如何使用它们。

最佳答案

您可以使用奇怪的循环模板模式 (CRTP) 来完成您想要的。基本思想是这样的:

template<class Pixel> struct TMonoPixel {
    ...

    // not virtual
    std::string GetSomeProperty() const {
        return static_cast<const Pixel&>(*this).GetSomeProperty();
    }

    Pixel operator+(const TMonoPixel& other) const {
        return Pixel(value + other.value);
    }
};

struct Mono8Pixel : TMonoPixel<Mono8Pixel> {
    using TMonoPixel::TMonoPixel;

    std::string GetSomeProperty() const {
        return "My name is Mono8Pixel";
    }
};

感谢隐式派生到基础的转换,现在您可以像这样使用它:

template<class T>
void foo(const TMonoPixel<T>& number) {
    std::cout << number.GetSomeProperty();    
}

Mono8Pixel i;
foo(i);

请注意,在 TMonoPixel 中,Pixel 是一个不完整的类型,因此您对它的使用方式有一些限制。例如,您不能这样做:

template<class Pixel> struct TMonoPixel {
    Pixel::Type operator+(const TMonoPixel& other);
};

struct Mono8Pixel : TMonoPixel<Mono8Pixel> {
    using Type = std::uint8_t;
};

类型特征是克服这些限制的有用技术:

struct Mono8Pixel;

template<class Pixel> struct ValueType;

template<> struct ValueType<Mono8Pixel> {
    using Type = std::uint8_t;
};

template<class Pixel> struct TMonoPixel {
    using Type = typename ValueType<Pixel>::Type;
    Type value;

    TMonoPixel(Type value) : value(value)
    {}

    Pixel operator+(const TMonoPixel& other) const {
        return Pixel(value + other.value);
    }
};

struct Mono8Pixel : TMonoPixel<Mono8Pixel> {
    using TMonoPixel::TMonoPixel;
};

Mono8Pixel(2) + Mono8Pixel(2) 的类型是Mono8Pixel

So I guess I'm asking whether these CRTP-based structs have standard layout after all of these changes to the type of value.

他们这样做:

static_assert(std::is_standard_layout_v<Mono8Pixel>);

完整示例:https://godbolt.org/z/8z0CKX

关于c++ - 如何使用运算符编写可继承的模板类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56482314/

相关文章:

c++ - FindVolumeClose() 访问冲突

c++ - 给模板参数包起别名

java - 即使在扩展后,如何观察自定义对象何时更改

具有纯虚函数的 C++ 继承

c++ - 我可以在 C++ 中定义枚举的大小吗?

C++:概念循环包含问题

数组中的 C++ 元素首先被正确读取,然后在没有更改的情况下给出 NaN 这怎么可能?

c++ - 为什么完全 `constexpr`启用数据结构会导致编译后的代码更大?

c++ - 没有enable_if 的表达式SFINAE?

java - 接口(interface)、继承和子类型