C++ C++;(以某种方式)将结构限制为父联合体大小

C++ C++;(以某种方式)将结构限制为父联合体大小,c++,unions,bit-fields,C++,Unions,Bit Fields,我试图创建一个大小可变的颜色类-给定一个由模板确定的值数组,我想为数组中的每个值创建命名别名,即: template<int C = 3, typename T = unsigned char> class Color { public: union { T v[C]; struct { T r, g, b, a; }; }; }; 模板 类颜色{ 公众: 联合{ tv[C]; 结构{ tr,g,b,a; }; }; }; 但是,如果我

我试图创建一个大小可变的颜色类-给定一个由模板确定的值数组,我想为数组中的每个值创建命名别名,即:

template<int C = 3, typename T = unsigned char>
class Color {
public:
  union {
    T v[C];
    struct {
      T r, g, b, a;
    };
  };
};
模板
类颜色{
公众:
联合{
tv[C];
结构{
tr,g,b,a;
};
};
};
但是,如果我尝试对C=3使用同一个类,则union要求大小为4字节(“a”成员)。或者,对于一个(名为a的结构,匿名T成员,大小在C>3时计算为1)使用数学上表示的位字段大小,编译器会发出一个允许的警告(根据,不可抑制),这不适用于更大规模的API

我如何允许单个类处理不同数量的变量,同时保留每个变量的名称,而不实现递归include宏魔术(尝试过这个,不应该有)。提前谢谢

编辑:要澄清问题,请回答以下任一问题即可解决此问题:

  • 抑制GCC的-fpPermissive错误(#pragma diagnostic ignored不适用于permissive)
  • 将联合或子结构的最大大小设置为不超过C字节
  • 对于未包含在C字节中的成员,允许位字段长度为0(GCC允许位字段长度的数学表达式,例如(C-3>0)?8:0;)
  • 通过其他方式(例如,mythical static_if())禁用C字节未覆盖的成员

您可以针对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;
  };
};
模板联合颜色;
模板
联合色{
tv[3];
结构{
tr,g,b;
};
};
模板
联合色{
tv[4];
结构{
tr,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_;
};
模板
类颜色{
公众:
使用值=T[C];
值&v(){return v_;}
常量值&v()常量{return v_;}
T&r(){return v_[0];}
T&g(){return v_[1];}
T&b(){return v_[2];}
模板::类型>
T&a()
{
返回v_[3];
}
常量T&r()常量{return v_[0];}
const T&g()const{return v_u[1];}
const T&b()const{return v_u[2];}
模板::类型>
常数T&a()常数
{
返回v_[3];
}
私人:
价值观;
};
然后您可以这样使用它:

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";
}
intmain()
{
彩色c3;
颜色c4;
c3.v()[0]=1;
c3.v()[1]=2;
c3.v()[2]=3;

std::cout好的,现在@VaughnCato在我之前已经有了这个答案,但是我仍然会使用
std::enable_if
发布我的答案。它将Color声明为struct,因为当所有内容都是公共的(并且没有充分的理由将数据成员[v]声明为私有的)时,拥有一个类是毫无意义的,添加模板别名以增加语法上的糖分,并使用
static\u assert
确保用户不会对模板参数使用奇怪的值。它还以稍微不同的方式使用
std::enable\u 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>;
#包括
#包括
模板
结构颜色
{

静态断言(nChans>=3 | | nChans所以如果我理解正确,您希望匿名结构的成员数量随模板参数
C
而变化?正确-理想情况下,类似静态断言的东西可以基于C>=4打开额外的a,x,y,z,w成员…您需要
tv[C]
的东西,或者你只是因为希望实现需要它而包含它?这很可能是UB所说的。如果我没有错的话,“r”不需要精确地映射到v[0](也不需要映射到v[3])。@CássioRenan这很复杂。我不确定你所说的“map”是什么意思,但是
r
需要存储在与
v[0]相同的地址
,因为数组的初始元素和标准布局类的初始成员都保证在没有任何填充的情况下存储。但是在设置
r
之后,读取
v[0]据我所知,
仍然是不允许的,反之亦然。标准没有定义这种行为,乐观主义者可能会基于程序不尝试这样做的假设进行优化。我同意,这是一种更好的
enable\u if
风格。非常感谢您回答中提供的详细信息-我不太愿意使用会员卡函数,但专用结构(部分模板)正是我所需要的。非常感谢!