C ++(以某种方式)将结构限制为父联合大小
C++ (Somehow) limit struct to parent union size
我正在尝试创建可变大小的颜色 class - 给定模板确定的值数组,我想为数组中的每个值创建命名别名,即:
template<int C = 3, typename T = unsigned char>
class Color {
public:
union {
T v[C];
struct {
T r, g, b, a;
};
};
};
但是,如果我尝试对 C=3 使用相同的 class,联合要求 4 字节的大小('a' 成员)。或者,使用 a 的数学表达的位域大小(名为 a 的结构,匿名 T 成员,大小在 C>3 时计算为 1),编译器发出一个许可警告(不可抑制,根据 In gcc, how to mute the -fpermissive warning? ),一些东西不适合更大规模 API。
我将如何允许单个 class 处理不同数量的变量,同时保留每个变量的名称并且不实施递归包含宏魔术(试过这个,不应该)。提前致谢!
编辑:为了澄清问题,回答以下任一问题将解决此问题:
- 抑制 GCC 的 -fpermissive 错误(#pragma diagnostic ignored 不适用于 permissive)
- 设置联合或子结构的最大大小不超过 C 字节
- 对于未被 C 字节覆盖的成员,允许位域长度为 0(GCC 允许位域长度的数学表达式,例如 (C-3 > 0)?8:0; )
- 通过其他方式禁用未被 C 字节覆盖的成员(即神话 static_if() )
您可以针对 C:
的不同情况对结构进行专门化
template <int C = 3, typename T = unsigned char> union Color;
template <typename T>
union Color<3,T> {
T v[3];
struct {
T r,g,b;
};
};
template <typename T>
union Color<4,T> {
T v[4];
struct {
T r,g,b,a;
};
};
请注意,匿名结构是非标准的。
如果可以使用成员函数,我认为那将是更好的方法:
template <int C,typename T>
class Color {
public:
using Values = T[C];
Values &v() { return v_; }
const Values &v() const { return v_; }
T& r() { return v_[0]; }
T& g() { return v_[1]; }
T& b() { return v_[2]; }
template <int C2 = C,
typename = typename std::enable_if<(C2>3)>::type>
T& a()
{
return v_[3];
}
const T& r() const { return v_[0]; }
const T& g() const { return v_[1]; }
const T& b() const { return v_[2]; }
template <int C2 = C,
typename = typename std::enable_if<(C2>3)>::type>
const T& a() const
{
return v_[3];
}
private:
Values v_;
};
然后您可以像这样使用它:
int main()
{
Color<3,int> c3;
Color<4,int> c4;
c3.v()[0] = 1;
c3.v()[1] = 2;
c3.v()[2] = 3;
std::cout <<
c3.r() << "," <<
c3.g() <<"," <<
c3.b() << "\n";
c4.v()[0] = 1;
c4.v()[1] = 2;
c4.v()[2] = 3;
c4.v()[3] = 4;
std::cout <<
c4.r() << "," <<
c4.g() << "," <<
c4.b() << "," <<
c4.a() << "\n";
}
好吧,现在@VaughnCato 在我面前提出了这个问题,但我仍然会 post 使用 std::enable_if
我的答案。它将 Color 声明为结构,因为当一切都是 public 时 class 真的没有意义(并且没有充分的理由将数据成员 [问题中的 v] 声明为私有),添加模板别名的语法糖更多一点,并使用 static_assert
来确保用户不会为模板参数使用奇怪的值。它还以稍微不同的方式使用 std::enable_if
,我认为这种方式更具可读性。
#include <type_traits>
#include <cstdint>
template<unsigned nChans, typename T = std::uint8_t>
struct Color
{
static_assert(nChans >= 3 || nChans <= 4, "number of color channels can only be 3 or 4");
// allow integral types only
//static_assert(std::is_integral<T>::value, "T has to be an integral type");
// also allow floating-point types
static_assert(std::is_arithmetic<T>::value, "T has to be an arithmetic (integral or floating-point) type");
T data[nChans];
T& r() { return data[0]; }
T& g() { return data[1]; }
T& b() { return data[2]; }
//template<typename U = T, typename EnableIfT = std::enable_if<(nChans == 4), U>::type> // C++11
template<typename U = T, typename EnableIfT = std::enable_if_t<(nChans == 4), U>> // C++14
T& a() { return data[3]; }
const T& r() const { return data[0]; }
const T& g() const { return data[1]; }
const T& b() const { return data[2]; }
//template<typename U = T, typename EnableIfT = std::enable_if<(nChans == 4), U>::type>
template<typename U = T, typename EnableIfT = std::enable_if_t<(nChans == 4), U>>
T const& a() const { return data[3]; }
};
template<typename T = std::uint8_t> using RgbColor = Color<3, T>;
template<typename T = std::uint8_t> using RgbaColor = Color<4, T>;
我正在尝试创建可变大小的颜色 class - 给定模板确定的值数组,我想为数组中的每个值创建命名别名,即:
template<int C = 3, typename T = unsigned char>
class Color {
public:
union {
T v[C];
struct {
T r, g, b, a;
};
};
};
但是,如果我尝试对 C=3 使用相同的 class,联合要求 4 字节的大小('a' 成员)。或者,使用 a 的数学表达的位域大小(名为 a 的结构,匿名 T 成员,大小在 C>3 时计算为 1),编译器发出一个许可警告(不可抑制,根据 In gcc, how to mute the -fpermissive warning? ),一些东西不适合更大规模 API。
我将如何允许单个 class 处理不同数量的变量,同时保留每个变量的名称并且不实施递归包含宏魔术(试过这个,不应该)。提前致谢!
编辑:为了澄清问题,回答以下任一问题将解决此问题:
- 抑制 GCC 的 -fpermissive 错误(#pragma diagnostic ignored 不适用于 permissive)
- 设置联合或子结构的最大大小不超过 C 字节
- 对于未被 C 字节覆盖的成员,允许位域长度为 0(GCC 允许位域长度的数学表达式,例如 (C-3 > 0)?8:0; )
- 通过其他方式禁用未被 C 字节覆盖的成员(即神话 static_if() )
您可以针对 C:
的不同情况对结构进行专门化template <int C = 3, typename T = unsigned char> union Color;
template <typename T>
union Color<3,T> {
T v[3];
struct {
T r,g,b;
};
};
template <typename T>
union Color<4,T> {
T v[4];
struct {
T r,g,b,a;
};
};
请注意,匿名结构是非标准的。
如果可以使用成员函数,我认为那将是更好的方法:
template <int C,typename T>
class Color {
public:
using Values = T[C];
Values &v() { return v_; }
const Values &v() const { return v_; }
T& r() { return v_[0]; }
T& g() { return v_[1]; }
T& b() { return v_[2]; }
template <int C2 = C,
typename = typename std::enable_if<(C2>3)>::type>
T& a()
{
return v_[3];
}
const T& r() const { return v_[0]; }
const T& g() const { return v_[1]; }
const T& b() const { return v_[2]; }
template <int C2 = C,
typename = typename std::enable_if<(C2>3)>::type>
const T& a() const
{
return v_[3];
}
private:
Values v_;
};
然后您可以像这样使用它:
int main()
{
Color<3,int> c3;
Color<4,int> c4;
c3.v()[0] = 1;
c3.v()[1] = 2;
c3.v()[2] = 3;
std::cout <<
c3.r() << "," <<
c3.g() <<"," <<
c3.b() << "\n";
c4.v()[0] = 1;
c4.v()[1] = 2;
c4.v()[2] = 3;
c4.v()[3] = 4;
std::cout <<
c4.r() << "," <<
c4.g() << "," <<
c4.b() << "," <<
c4.a() << "\n";
}
好吧,现在@VaughnCato 在我面前提出了这个问题,但我仍然会 post 使用 std::enable_if
我的答案。它将 Color 声明为结构,因为当一切都是 public 时 class 真的没有意义(并且没有充分的理由将数据成员 [问题中的 v] 声明为私有),添加模板别名的语法糖更多一点,并使用 static_assert
来确保用户不会为模板参数使用奇怪的值。它还以稍微不同的方式使用 std::enable_if
,我认为这种方式更具可读性。
#include <type_traits>
#include <cstdint>
template<unsigned nChans, typename T = std::uint8_t>
struct Color
{
static_assert(nChans >= 3 || nChans <= 4, "number of color channels can only be 3 or 4");
// allow integral types only
//static_assert(std::is_integral<T>::value, "T has to be an integral type");
// also allow floating-point types
static_assert(std::is_arithmetic<T>::value, "T has to be an arithmetic (integral or floating-point) type");
T data[nChans];
T& r() { return data[0]; }
T& g() { return data[1]; }
T& b() { return data[2]; }
//template<typename U = T, typename EnableIfT = std::enable_if<(nChans == 4), U>::type> // C++11
template<typename U = T, typename EnableIfT = std::enable_if_t<(nChans == 4), U>> // C++14
T& a() { return data[3]; }
const T& r() const { return data[0]; }
const T& g() const { return data[1]; }
const T& b() const { return data[2]; }
//template<typename U = T, typename EnableIfT = std::enable_if<(nChans == 4), U>::type>
template<typename U = T, typename EnableIfT = std::enable_if_t<(nChans == 4), U>>
T const& a() const { return data[3]; }
};
template<typename T = std::uint8_t> using RgbColor = Color<3, T>;
template<typename T = std::uint8_t> using RgbaColor = Color<4, T>;