5

それぞれ長さが異なる固定数のデータ型を格納するテンプレート化された基本クラスを作成しようとしていました。これが私がやろうとしていたことの単純化されたバージョンです:

template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
   public:

  EncapsulatedObjectBase();

  ~EncapsulatedObjectBase();

  double m_real[NR0];
  int m_int[NINT];
}

ええ...テンプレートパラメータをゼロにすることができるので、オブジェクトの長さゼロの配列を宣言します。このベースには複数の派生クラスがあり、それぞれが独自の数の変数を定義します。2つの質問があります:

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フラグを使用する必要があります。だから私はまだこれがどれほど危険かわかりません。振り返ってみると、それはあまり良い考えではないに違いないように感じます...私がそれをうまくやることができればそれはかなり役に立ちますが。

4

3 に答える 3

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++にどれほど適用できるかはわかりません。ただし、クラスをサブクラス化しない限り、おそらく問題ありません。

于 2013-01-18T00:55:55.777 に答える
2

ゼロサイズの配列は、C++では実際には違法です。

[C++11: 8.3.4/1]: [..]定数式(5.19)が存在する場合、それは積分定数式であり、その値はゼロより大きくなければなりません。定数式は、配列の境界(要素数)を指定します。定数式の値が、の場合N、配列にはに番号が付けられたN要素があり、の識別子の型は「 Tの派生宣言子型リスト配列」です。[..]0N-1DN

このため、クラステンプレートは、GCC4.1.2またはGCC4.7.2の引数を使用して適切なフラグを使用してインスタンス化することはできません。0,0

template< int NINT, int NR0 >
class EncapsulatedObjectBase
{
   public:

  EncapsulatedObjectBase();

  ~EncapsulatedObjectBase();

  double m_real[NR0];
  int m_int[NINT];
};

int main()
{
   EncapsulatedObjectBase<0,0> obj;
}

t.cpp:'EncapsulatedObjectBase <0、0>'のインスタンス化:
t.cpp:17:ここからインスタンス化
10行目:エラー:ISO C ++
は、-Wfatal-errorsが原因でゼロサイズの配列のコンパイルを終了することを禁止しています。

clang 3.2によると:

source.cpp:10:17:警告:ゼロサイズの配列は拡張機能です[-Wzero-length-array]

(いずれの場合も、そのようなクラスをインスタンス化しようとするまで、エラーは発生しないことに注意してください。)

それで、それは良い考えですか?いいえ、そうではありません。いずれかの引数がである場合は、クラステンプレートのインスタンス化を禁止することをお勧めします0。また、長さがゼロの配列が必要な理由を確認し、設計の調整を検討します。

于 2013-01-18T01:09:33.840 に答える
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];
};

備考: クラス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>
   {
     ....
   };
于 2013-01-18T02:39:11.690 に答える