我试图编写一个模板化的基类来存储固定数量的数据类型,每种类型都有不同的长度。这是我试图做的很简单的版本:
template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
public:
EncapsulatedObjectBase();
~EncapsulatedObjectBase();
double m_real[NR0];
int m_int[NINT];
}
是的......所以模板参数可以为零,从而声明一个零长度的对象数组。这个库将有多个派生类,每个派生类定义自己的变量数。我有两个问题:
1)这种方法是否存在根本缺陷?
2)如果是这样的话......当我实例化零长度数组时,为什么icc13或gcc4.7.2没有给我警告?对于gcc我使用-wall和-wextra -wabi。缺乏警告让我觉得这种事情还行。
编辑:
以下是显示我所说内容的文件内容:
#include <iostream>
template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
public:
EncapsulatedObjectBase(){}
~EncapsulatedObjectBase(){}
double m_real[NR0];
int m_int[NINT];
};
class DerivedDataObject1 : public EncapsulatedObjectBase<2,0>
{
public:
DerivedDataObject1(){}
~DerivedDataObject1(){}
inline int& intvar1() { return this->m_int[0]; }
inline int& intvar2() { return this->m_int[1]; }
};
class DerivedDataObject2 : public EncapsulatedObjectBase<0,2>
{
public:
DerivedDataObject2(){}
~DerivedDataObject2(){}
inline double& realvar1() { return this->m_real[0]; }
inline double& realvar2() { return this->m_real[1]; }
};
int main()
{
DerivedDataObject1 obj1;
DerivedDataObject2 obj2;
obj1.intvar1() = 12;
obj1.intvar2() = 5;
obj2.realvar1() = 1.0e5;
obj2.realvar2() = 1.0e6;
std::cout<<"obj1.intvar1() = "<<obj1.intvar1()<<std::endl;
std::cout<<"obj1.intvar2() = "<<obj1.intvar2()<<std::endl;
std::cout<<"obj2.realvar1() = "<<obj2.realvar1()<<std::endl;
std::cout<<"obj2.realvar2() = "<<obj2.realvar2()<<std::endl;
}
如果我用“g ++ -Wall -Wextra -Wabi main.cpp”编译它,我没有得到任何警告。我必须使用-pedantic标志来获取警告。所以我仍然不知道这是多么不安全。回想起来,我觉得它一定不是一个好主意......虽然如果我能逃脱它会非常有用。
答案 0 :(得分:3)
在C中,使用零大小的数组作为结构的最后一个成员实际上是合法的,并且通常在结构将以某种动态创建的内联数据结束时使用,这些数据在编译时是未知的。换句话说,我可能会有像
这样的东西struct MyData {
size_t size;
char data[0];
};
struct MyData *newData(size_t size) {
struct MyData *myData = (struct MyData *)malloc(sizeof(struct MyData) + size);
myData->size = size;
bzero(myData->data, size);
return myData;
}
现在可以将myData->data
字段作为指向动态大小数据
那就是说,我不知道这种技术对C ++有多适用。但只要你从不继承你的班级,它就可能很好。
答案 1 :(得分:1)
零大小的数组在C ++中实际上是非法的:
[C++11: 8.3.4/1]:
[..] 如果常量表达式(5.19)存在,它应该是一个整数常量表达式及其值应大于零。常量表达式指定数组中的(元素数)的边界。如果常量表达式的值为N
,则数组具有编号为N
到0
的{{1}}个元素,N-1
的标识符类型为“ {em> derived-declarator-type-listD
T“数组。 [..]
出于这个原因,您的类模板无法使用参数N
in GCC 4.1.2或in GCC 4.7.2进行实例化,并使用合理的标记:
0,0
t.cpp:实例化'EncapsulatedObjectBase&lt; 0,0&gt;': t.cpp:17:从这里实例化 第10行:错误:ISO C ++禁止零大小的阵列
由于-Wfatal-errors而导致编译终止。
clang 3.2说:
source.cpp:10:17:警告:零大小数组是扩展名[-Wzero-length-array]
(请注意,在任何情况下,在尝试实例化此类之前,不会出现任何错误。)
那么,这是个好主意吗?不,不是真的。当任一参数为template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
public:
EncapsulatedObjectBase();
~EncapsulatedObjectBase();
double m_real[NR0];
int m_int[NINT];
};
int main()
{
EncapsulatedObjectBase<0,0> obj;
}
时,我建议禁止对您的类模板进行实例化。我还会看看为什么你想要零长度数组并考虑调整你的设计。
答案 2 :(得分:1)
1)添加到您的类 C ++ 11 static_assert 或 BOOST_STATIC_ASSERT 的声明,您将获得编译时诊断的零长度阵列:
....
BOOST_STATIC_ASSERT(NR0 > 0);
BOOST_STATIC_ASSERT(NINT > 0);
double m_real[NR0];
int m_int[NINT];
};
2)使用 std :: array 或 boost :: array ,您将在此类代码中对索引溢出问题进行运行时诊断(在调试模式下):
BOOST_STATIC_ASSERT(NR0 > 0);
BOOST_STATIC_ASSERT(NINT > 0);
boost::array<double, NR> m_real; //double m_real[NR0];
boost::array<int, NINT> m_int; //int m_int[NINT];
};
注: class boost :: array 具有零大小数组的专业化
3)对于数组大小,使用 size_t 但不使用int。
你的设计非常危险:
DerivedDataObject1 a;
a.m_real[2] = 1; // size of m_real == 0 !!!
我认为更改类EncapsulatedObjectBase的设计会更好。可能会更好地使用:
template<typename T, size_t N> class EncapsulatedObjectBase
{
....
};
class DerivedDataObject1 : public EncapsulatedObjectBase<int,2>
{
....
};
class DerivedDataObject2 : public EncapsulatedObjectBase<double,2>
{
....
};
class DerivedDataObject3 : public EncapsulatedObjectBase<double,2>
, public EncapsulatedObjectBase<int,2>
{
....
};