c++ 如何使模板之间具有继承性和联合性?

pnwntuvh  于 2023-03-14  发布在  其他
关注(0)|答案(3)|浏览(127)

我有以下两个对象。我想知道是否有一种方法可以将Pixel作为PixelBGR的基类,以便可以使用任何操作符(+、-、*、/、[]等)而无需重新定义它们?

template<class T, std::size_t N>
struct Pixel
{
    T ch[N];

    inline T& operator[](const int x)
    {
        return ch[x];
    }
};

template<class T>
struct PixelBGR
{
    union
    {
        struct
        {
            T b;
            T g;
            T r;
        };
        T ch[3];
    };

    inline T& operator[](const int x)
    {
        return ch[x];
    }
};

EDIT:正如π ντα ε所建议的,这里有更多关于我想做什么的细节。

我试图有一个泛型类Pixel,这将是处理任何类型或大小的模板。
通常为1、2、3、4、8或16。带有的类定义了一些operator,如+、-、* 等。
由于大多数情况下,Pixel<T,3>是BGR像素,我希望定义快速访问rgb以避免混淆,但仍将其存储为BGR。
但是派生类还应该提供Operator,它将是基于N的泛型。

EDIT 2:通过阅读SergeyA的注解,我忘记了说结构体Pixel一定不能改变大小。

所以我认为balki的答案是最好的,通过使用成员函数。我试图使它与变量,以避免太多的字符 * 即:添加()*,但似乎太复杂了。我仍在调查CRTP,但我不太明白,我阅读。

8zzbczxx

8zzbczxx1#

回答所提出的问题,这应该给予OP操作符的重用,而没有任何未定义的行为:

#include <cstddef>

template<class T, std::size_t N>
struct Pixel
{
    T ch[N];

    inline T& operator[](const int x)
    {
        return ch[x];
    }
    Pixel& operator+= (const Pixel& ) { return *this;}

};

template<class T, std::size_t N>
Pixel<T, N> operator+ (const Pixel<T, N>& l, const Pixel<T, N>& r);

template<class T>
struct BgrPixel : Pixel<T, 3> {
    using base = Pixel<T, 3>;
    using base::base;
    BgrPixel(const base& b) : base(b) { };
    T& b = base::ch[0];
    T& g = base::ch[1];
    T& r = base::ch[2];
};

BgrPixel<int> a, b;

BgrPixel<int> c = a + b;

另一种方法是将b()、g()和r()作为成员函数,但这需要将它们作为函数访问,还需要它们的常量和非常量版本。
然而,这样做的好处是结构体的大小不会增加,并且拷贝分配可以自然地工作(反过来,这可以通过提供自定义拷贝分配来解决)。

pinkon5k

pinkon5k2#

在这种情况下,奇怪的循环模板模式(CRTP)会很好地工作。在CRTP中,派生类被用作基类的模板参数。大卫Vandevoorde和Nicolai M. Josuttis所著的C++模板-完整指南中的第16.3章奇怪的循环模板模式(CRTP)更详细地解释了这些事情。
从下面的评论中可以看出,使用union{struct{...}...}会导致未定义行为(UB),但对此有一些矛盾的观点。据我所知,它是一个gnu扩展,几乎所有编译器都支持它。例如,glm经常使用union-structs
作为替代方法,您可以为r,g,b变量使用别名(引用)。

#include <iostream>

template<typename T, std::size_t N, template<typename,std::size_t> class B >
struct Pixel
{
    B<T,N> *crtp = static_cast<B<T,N>*>(this);

    T& operator[](std::size_t x)
    {
        return crtp->ch[x];
    }

    Pixel& operator = (const Pixel &t)
    {
        crtp->ch[0] = t.crtp->ch[0];
        crtp->ch[1] = t.crtp->ch[1];
        crtp->ch[2] = t.crtp->ch[2];
        return *crtp;
    }

    B<T,N> operator + (const B<T,N> &t)
    {
        B<T,N> tmp;
        tmp[0] = crtp->ch[0] + t.crtp->ch[0];
        tmp[1] = crtp->ch[1] + t.crtp->ch[1];
        tmp[2] = crtp->ch[2] + t.crtp->ch[2];
        return tmp;
    }

    B<T,N> operator - (const B<T,N> &t)
    {
        B<T,N> tmp;
        tmp[0] = crtp->ch[0] - t.crtp->ch[0];
        tmp[1] = crtp->ch[1] - t.crtp->ch[1];
        tmp[2] = crtp->ch[2] - t.crtp->ch[2];
        return tmp;
    }
};

template<typename T, std::size_t N=3>
struct PixelBGR : Pixel<T, N, PixelBGR>
{
    T ch[3];
    T &r;
    T &g;
    T &b;

    PixelBGR() : ch{},r(ch[0]),g(ch[1]),b(ch[2])
    {}
    PixelBGR& operator = (const PixelBGR &p)
    {
        ch[0] = p.ch[0];
        ch[1] = p.ch[1];
        ch[2] = p.ch[2];
        return *this;
    }
};

int main()
{
    PixelBGR<int> p;

    p.r = 25;
    p.g = 14;
    p.b = 58;

    std::cout<< p[0] <<" , "<<p[1]<<" , "<<p[2] <<std::endl;

    PixelBGR<int> q;
    q = p;
    std::cout<< q[0] <<" , "<<q[1]<<" , "<<q[2] <<std::endl;

    PixelBGR<int> res1;
    res1 = q + p;
    std::cout<< res1.r <<" , "<<res1.g<<" , "<<res1.b <<std::endl;

    PixelBGR<int> res2;
    res2 = q - p;
    std::cout<< res2.r <<" , "<<res2.g<<" , "<<res2.b <<std::endl;
}

结果:

25 , 14 , 58
25 , 14 , 58
50 , 28 , 116
0 , 0 , 0

使用参考文献的示例:https://rextester.com/AZWG4319
使用union-struct的示例:https://rextester.com/EACC87146

cyej8jka

cyej8jka3#

首先感谢你们所有人的建议,特别感谢@Constantinos Glynos、@balki和@SergeyA提供的示例,他们帮助我实现了符合我需求的解决方案。
我实现了BGR和BGRA,以显示N工作正常,现在我只需要实现我需要的所有运算符和函数。

请随意编辑,或告诉我是否有问题。
像素.h

#include <cstdio>   // std::size_t
#include <iostream>     // std::cout

template<typename T, std::size_t N, template<typename, std::size_t> class B >
struct Pixel
{
    T ch[N];

    // ==============================================================
    // Overload the accessor (so .ch[0] == direct access with [0].
    T& operator[](std::size_t x){ return ch[x]; }

    // ==============================================================
    // Copy-assignement
    Pixel& operator=( const Pixel &t )
    {
        for ( int i = 0; i < N; i++ )
            ch[i] = t.ch[i];
        return *this;
    }

    // ==============================================================
    // Operator
    B<T, N> operator+( const B<T, N> &t )
    {
        B<T, N> tmp;
        for ( int i = 0; i < N; i++ )
            tmp[i] = ch[i] + t.ch[i];
        return tmp;
    }

    B<T, N> operator-( const B<T, N> &t )
    {
        B<T, N> tmp;
        for ( int i = 0; i < N; i++ )
            tmp[i] = ch[i] - t.ch[i];
        return tmp;
    }

    template<typename T, std::size_t N, template<typename, std::size_t> class B >
    friend std::ostream& operator<<( std::ostream& os, const Pixel &t );
};

// To print the vector
template<typename T, std::size_t N, template<typename, std::size_t> class B >
std::ostream& operator<<( std::ostream& os, const B<T, N> &t )
{
    os << "Pixel: (" << t.ch[0];
    for ( int i = 1; i < N; i++ )
        os << ", " << t.ch[i];
    os << ")";
    return os;
}



template<typename T, std::size_t N = 3>
struct BGR : Pixel<T, N, BGR>
{
    T& b() { return ch[0]; }
    T& g() { return ch[1]; }
    T& r() { return ch[2]; }
};

template<typename T, std::size_t N = 4>
struct BGRA : Pixel<T, N, BGRA>
{
    T& b() { return ch[0]; }
    T& g() { return ch[1]; }
    T& r() { return ch[2]; }
    T& a() { return ch[3]; }
};

主要.cpp

int main() {
    std::cout << "Sizeof a float BGR: " << sizeof(BGR<float>) << std::endl;
    std::cout << "Sizeof a float BGRA: " << sizeof(BGRA<float>) << std::endl;

    BGR<int> p;
    p.r() = 25;
    p.g() = 14;
    p.b() = 58;

    std::cout << p << std::endl;
    std::cout << p[0] << " , " << p[1] << " , " << p[2] << std::endl;
    std::cout << p.b() << " , " << p.g() << " , " << p.r() << std::endl;

    BGR<int> q;
    q = p;
    std::cout << q[0] << " , " << q[1] << " , " << q[2] << std::endl;

    BGR<int> res1;
    res1 = q + p;
    std::cout << res1.r() << " , " << res1.g() << " , " << res1.b() << std::endl;

    BGR<int> res2;
    res2 = q - p;
    std::cout << res2.r() << " , " << res2.g() << " , " << res2.b() << std::endl;

    BGRA<float> a;
    a.r() = 255.0f;
    a.g() = 0.0f;
    a.b() = 0.0f;
    a.a() = 128.5f;

    BGRA<float> b = a;
    std::cout << a << std::endl;

    return 0;
}

相关问题