c模板:避免零长度数组而不占用额外空间

注意:

根据liveworkspace.org,对于最近版本的g(4.7.2),clang(3.2)和icc(13.0.1),这个问题的答案是有效的,但根据Stephen Lin的评论,它依赖于empty base optimization和std的实现: :元组.

原来的问题:

如果我有一个如下所示的模板结构:

template<
    class T1, unsigned short N1,
    class T2, unsigned short N2
>
struct ComboThree {
    T1 data_1[N1];
    T2 data_2[N2];
};

我可以通过专门化来避免零长度数组和额外的对齐填充:

template<class T1, class T2>
struct ComboThree<T1, 0, T2, 0> {
};

template<class T1, class T2, unsigned short N2>
struct ComboThree<T1, 0, T2, N2>
{
    T2 data_2[N2];
};

template<class T1, unsigned short N1, class T2>
struct ComboThree<T1, N1, T2, 0>
{
    T1 data_1[N1];
};

但是当TX / NX对的X变得更大时,像这样的专业化变得麻烦.在我的项目中,不同组合的实际数量可能会少于五个,所以我可能最终根本不使用模板,但我很好奇:

有没有办法使用TEMPLATE MAGIC来避免零长度数组,同时避免占用任何额外的空间?

例如,这个:

template<class T, unsigned short N>
struct Array {
    T data[N];
};

template<class T>
struct Array<T, 0> {};

template<
    class T1, unsigned short N1,
    class T2, unsigned short N2
>
struct ComboTwo {
    Array<T1, N1> data_1;
    Array<T2, N2> data_2;
};

避免零长度数组,但空结构占用额外的空间.另一方面,这个:

template<class T, unsigned short N>
struct Array {
    T data[N];
};

template<class T>
struct Array<T, 0> {};

template<
    class T1, unsigned short N1,
    class T2, unsigned short N2
>
struct ComboFour : Array<T1, N1>, Array<T2, N2> {};

似乎做了我想做的事(是吗?),但我不知道如何访问数组中的数据<>基础结构在我的程序中. It also has other limitations noted by Stephen Lin below.

最佳答案 这不是一个完整的答案,但它很难适应评论.要访问ComboFour的子对象,如果您真的想要执行最后一个选项,则需要以下丑陋的语法:

ComboFour<int, 2, float, 1> cf;
cf.Array<int, 2>::data[0] = 0;
cf.Array<int, 2>::data[1] = 1;
cf.Array<float, 1>::data[0] = 2.0f;

您可以使用一些访问器功能来清理它,但它仍然不会很好.

但更大的问题是以下是一个错误:

ComboFour<int, 1, int, 1> cf2 // fails to compile

因为同一个班级不能作为父母两次使用.

(另请注意,ComboFour的数组< T,0>子对象可能会也可能不会占用零空间,这称为“空基优化”,并且标准允许但不是必需的.)

可能有一些方法来解决第二个问题……我认为继承自std :: tuple< …>如果你真的不得不这样做(可能是也可能没有使用你的标准库实现在内部通过你的标准库实现的空基本优化实现),这可能是最简单的方法,但它会使语法更加丑陋.

编辑:这适用于GCC 4.7.2

template<
    class T1, unsigned short N1,
    class T2, unsigned short N2,
    class T3, unsigned short N3
>
struct Combo : std::tuple<Array<T1, N1>, Array<T2, N2>, Array<T3, N3>>
{
};

然后…

Combo<int, 2, int, 2, float, 3> c;
std::get<0>(c).data[0] = 0;
std::get<0>(c).data[1] = 1;
std::get<1>(c).data[0] = 2;
std::get<1>(c).data[1] = 3;
std::get<2>(c).data[0] = 0.0;
std::get<2>(c).data[1] = 1.0;
std::get<2>(c).data[2] = 2.0;

assert(sizeof(Combo<int, 0, int, 0, float, 1>) == sizeof(float));

(老实说,标准库或多或少需要空基优化才能使用,所以即使不需要它我也会感到惊讶,如果最近的编译器不支持它;无论是否std :: tuple< … >在书面中正确利用这种优化是另一回事.)

点赞