我有以下两个对象。我想知道是否有一种方法可以将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像素,我想定义对r
,g
和b
的快速访问以避免混淆,但是仍将其存储为BGR。
但是派生类还应提供基于N
通用的Operator。
EDIT2 :通过阅读SergeyA的注释,我忘了说结构Pixel
不能改变大小。
因此,我认为通过使用成员函数,最佳答案是最好的。我试图用变量来避免过多的char ,即:添加(),但这似乎太复杂了。我仍在研究CRTP,但我做得不好,我正在阅读。
答案 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;
}