C ++(不知何故)将struct限制为父联合大小

时间:2015-03-09 19:36:45

标签: 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;
    };
  };
};

但是,如果我尝试对C = 3使用相同的类,则union要求4字节的大小('a'成员)。或者,使用数学表达的位域大小为(结构名为a,匿名T成员,大小在C> 3时评估为1),编译器发出一个允许警告(不可抑制,根据In gcc, how to mute the -fpermissive warning?),某事不适合更大规模的API。

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

编辑:为了澄清这个问题,以下任何一个问题的答案都可以解决这个问题:

  • 抑制GCC的 - 错误(#pragma诊断忽略不适用于许可)
  • 设置union或child struct的最大大小不超过C字节
  • 对于未被C字节覆盖的成员,允许位域长度为0(GCC允许数据表达式用于位域长度,例如(C-3> 0)?8:0;)
  • 通过其他方式禁用未被C字节覆盖的成员(即神话中的static_if())

2 个答案:

答案 0 :(得分:4)

您可以针对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";
}

答案 1 :(得分:2)

好的,现在@VaughnCato已经摆脱了我的这个问题,但我仍然会使用std::enable_if发布我的答案。它将Color声明为struct,因为当一切都是公共的时候(并且没有充分的理由声明数据成员[问题中的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>;