Как иметь наследование между шаблоном с объединением? - PullRequest
1 голос
/ 01 апреля 2019

У меня есть следующие два объекта.Мне интересно, есть ли способ иметь 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];
    }
};

РЕДАКТИРОВАТЬ : В соответствии с предложением πάντα ῥεῖ, здесь более подробно о том, что я пытаюсь сделать.

Я пытаюсь создать общий класс Pixel, который будет шаблономдля обработки любого типа или размера.

Обычно это 1,2,3,4,8 или 16. Класс с определяет некоторые operator, такие как +, -, * и т. д.

Поскольку большую часть времени Pixel<T,3> является пикселем BGR, я бы хотел определить быстрый доступ к r, g и b, чтобы избежать путаницы, но все же сохранить его как BGR.

Но производный класс также должен предоставлять Оператор, который будет универсальным на основе N.

EDIT2 : Читая комментарий SergeyA, я забыл сказать, что структураPixel не должен изменять размер.

Поэтому я считаю, что балки лучше всего использовать с помощью функции-члена.Я пытался сделать это с переменными, чтобы избежать слишком большого количества символов , т. Е. Добавить () , но это кажется слишком сложным ни для чего.Я все еще расследую CRTP, но я не понимаю, читаю.

Ответы [ 3 ]

1 голос
/ 01 апреля 2019

Шаблон Curily Recurring Template Pattern (CRTP) будет хорошо работать в этом случае.В CRTP класс Derived используется в качестве аргумента шаблона для базового класса.Глава 16.3. Шаблон любопытного повторения шаблона (CRTP) из шаблонов C ++ - Полное руководство, написанное Дэвидом Вандевурдом и Николаем М. Йосуттисом, объясняет вещи более подробно.

Из комментариев ниже, использование 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

1 голос
/ 01 апреля 2019

Отвечая на вопрос в том виде, в котором он был задан, это должно привести к повторному использованию операторов 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;

Alterinative будет иметь функции b (), g () и r () в качестве члена-члена, но для этого потребуется доступ к ним как функциям. Вам также потребуются их постоянные и неконстантные версии.

Преимущества, однако, заключаются в том, что размер структуры не будет увеличен, и назначение копирования будет работать естественным образом (что, в свою очередь, может быть решено путем предоставления пользовательского назначения копирования).

0 голосов
/ 02 апреля 2019

Прежде всего, спасибо всем за советы и особую благодарность @Constantinos Glynos, @balki и @SergeyA за предоставленный ими пример, который помогает мне найти решение, соответствующее моим потребностям.

Iреализовал 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...