如何在模板与联合之间继承?

时间:2019-04-01 19:25:43

标签: c++ templates union

我有以下两个对象。我想知道是否有一种方法可以将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];
    }
};

编辑:如πάνταῥεῖ所建议,此处提供了有关Im试图做的事情的更多详细信息。

我正在尝试创建一个通用类Pixel,它将作为处理任何类型或大小的模板。

通常为1,2,3,4,8或16。具有的类定义了一些operator,例如+,-,*等。

由于在大多数情况下,Pixel<T,3>是BGR像素,我想定义对rgb的快速访问以避免混淆,但是仍将其存储为BGR。

但是派生类还应提供基于N通用的Operator。

EDIT2 :通过阅读SergeyA的注释,我忘了说结构Pixel不能改变大小。

因此,我认为通过使用成员函数,最佳答案是最好的。我试图用变量来避免过多的char ,即:添加(),但这似乎太复杂了。我仍在研究CRTP,但我做得不好,我正在阅读。

3 个答案:

答案 0 :(得分:1)

回答所提出的问题,这将使操作符在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()作为成员函数,但这需要您将它们作为函数进行访问。您还需要它们的const和非const版本。

但是,好处是不会增加该结构的大小,并且副本分配将自然工作(这反过来可以通过提供自定义副本分配来解决)。

答案 1 :(得分:1)

在这种情况下,很好地重复使用模板模式(CRTP)可以很好地工作。在CRTP中,派生类用作基类的模板参数。 David 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

使用并集结构的示例:https://rextester.com/EACC87146

答案 2 :(得分:0)

首先感谢大家的建议,特别感谢@Constantinos Glynos,@ balki和@SergeyA提供的示例,这些帮助我实现了满足我需要的解决方案。

我实现了BGR和BGRA,以证明N可以正常工作,现在我只需要实现所有运算符和所需的功能即可。

请随时进行编辑,或告诉我这是否有问题。

Pixel.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]; }
};

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