web-dev-qa-db-ja.com

なぜstd :: is_struct型の特性がないのですか?

Tが使用できるクラスであるかどうかを確認するために見ました:

bool isClass = std::is_class<T>::value;

クラスと構造体の両方に対してtrueを返します。私はC++ではほとんど同じものであることを知っていますが、型特性でそれらが区別されない理由を知りたいです。この違いをチェックするのは常に役に立たないのですか、それとも私が理解できない理由が他にありますか?

55
Jepessen

クラスと構造体の両方に対してtrueを返します。私はC++ではほとんど同じものであることを知っていますが、型特性でそれらが区別されない理由を知りたいです。

残念ながら、これはC++の一般的な誤解です。根本的な誤解からくる場合もあれば、英語のあいまいさからくる場合もあります。不正確なコンパイラ診断、不適切な書物、間違ったSO回答…

あなたはおそらく次のようなものを読んだでしょう:

「メンバーとベースのデフォルトの可視性を除いて、構造体とクラスの間にC++の違いはありません。」

identityequalityの概念はフレーズを使用する際に区別が難しいため、この文章は誤解を招く意味で解釈できます。 「違いなし」など。

実際、C++には1985年以来構造体がありません。クラスのみがあります。

キーワードclassとキーワードstructで宣言する型の種類はclasses です。期間。キーワードstruct、およびそのキーワードを使用してクラスを定義する際のデフォルトである可視性ルールは、Cとの後方互換性のためだけに保持されましたが、それは構文上のことです。結果の型が実際に異なる種類になることはありません。

文字通り作成するものがないため、型特性は区別されません。

次のような空の定義のセマンティクスの違いを区別することは不可能です

class C {
public:

};

から

struct S {

};

または同様に

class C {

};

そして

struct S {
private:

};

struct vs classキーワードを除いて、動作の違いは検出できません。 this Q&A も参照してください。

:@KyleStrandで述べたように、派生には明示的なアクセス指定子も必要なので、S : private Base {};C : Base {};は同等で、同じですS : Base {};およびC : public Base {};として。ここで、Sは構造体、Cはクラス、Baseはどちらでもかまいません。

27
TemplateRex

それらは同じものです。唯一の違い(デフォルトのメンバーの可視性)は、コンパイル時にのみ存在します。それ以外の場合は、structclassの間にまったく違いはありません。

ETA:おそらく必要なのは std::is_pod 。これは、クラスが「単純な古いデータ型」であるかどうかを示します。この質問に関する議論と解説の多くは、これが区別があるべきだと考える人が実際に望むものであることを示しているようです。

23
Rob K

他の人たちは、C++ではキーワードstructclassはメンバーの可視性の違いを除いて同じ意味を持つことを正しく指摘しています。

このように定義された集約型を「構造体」、「クラス」、または「weiruewzewiruz」と呼ぶかどうかはあなた次第です。コミュニケーションの観点から、一般的に確立された慣習に従うことをお勧めします。そのため、「weiruewzewiruz」に反対することをお勧めします。

また、セマンティックの違いをWordの選択のガイドラインとして使用することをお勧めします。 structの使用は、多くの内部ロジックと不変条件を持たない単純な集約データに対してより一般的です。典型的な用途はstruct point { float x; float y; };です。そのようなタイプは、文献ではしばしば「構造」または「構造」と呼ばれます。 C++でfprintfを使用している人が最初の引数を「FILE構造体へのポインタ」と呼んだとしても驚くことではありません。 FILEは、「より効果的なC++」の項目34でスコットマイヤーズが意味するものの例です。

両方の言語[CおよびC++ -p.a.s]でコンパイルされる構造定義は、両方のコンパイラーによって同じ方法でレイアウトされると想定しても安全です。

自然言語に関しては、Wordの選択「構造」は偶然ではありません。マイヤーズは、ビットレベルまで、両方の言語で同一のセマンティクスを持つ単純な古いデータ集合体について語っています。

プログラミング言語に関しては、問題のデータ集合のC++定義がキーワードstructまたはclass(パブリックアクセス指定子を使用)を使用したかどうかは関係ありません。集約のC++セマンティクスはC構造体のセマンティクスであるため、structがおそらくより自然な選択です。また、structを使用すると、CソースとC++ソースの両方で1つの型定義を簡単に共有できます。

C++標準は、相互運用性の場合だけでなく、自然言語とプログラミング言語の両方で「構造体」と「構造体」を使用します。1.7/ 5:「宣言された構造体」または3.2/4 struct X; // declare X as a struct type。最も興味深いのは9/8であり、相互運用基準の土台となります。

8標準レイアウト構造体は、クラスキー構造体またはクラスキークラスで定義された標準レイアウトクラスです。 [...]

これを読んでいる人がC++に構造体がないと主張する方法は、私を超えています。 「struct」と「class」という用語は相互に関連して明示的に設定されているため、これは明らかに編集エラーではありません。


しかし、Wordの選択や好みの問題よりも興味深いのは、明白でテスト可能な違いです。 C++アグリゲートは、どのような状況でC structに匹敵し、互換性がありますか?この質問があなたの質問の根底にあったのでしょうか?引用に記載されている標準レイアウトが基準です。 9/7で詳しく説明されており、基本的には

  • 継承階層の1つのクラスのみが非静的データメンバー定義を持つことができます(おそらく、標準ではそのような階層の異なるレベルで定義されたデータ要素の順序を指定したくないためです)。
  • 仮想関数または仮想基底クラスは許可されません(実行時情報に必要な追加のインスタンスデータのため)。
  • すべてのメンバーは同じ「アクセス制御」を持ちます(パブリック、保護、プライベートのいずれか。おそらく実装はアクセス制御によって自由に注文できるためです)。

それから標準は言う

9 [注:標準レイアウトクラスは、他のプログラミング言語で記述されたコードとの通信に役立ちます。レイアウトは9.2で指定されています。

もちろん、Cでコンパイルする構造体定義はこれらの基準を満たしているため、Scott Meyersの主張です。 stdio.hのFILEは、目立った例ですが、それほど重要ではありません。オブジェクトのレイアウトは実装に依存しており、コンパイラオプションを変更するだけで変更される可能性があるため、標準では保証されないことに注意してください。

クラスに標準レイアウトがあるかどうかは、型特性std::is_standard_layout<T>でテストできます。 cppreferenceの例 に触発された次のプログラムは、標準で規定されている主なケースをチェックします。

#include <cstdio>
#include <typeinfo>
#include <type_traits>

using namespace std;

struct funcOnlyT // fine
{
    int f();
};

class podT {   // "class" is ok
    int m1;
    int m2;
};

struct badAccessCtrlT { // bad: public/private
    int m1;
private:
    int m2;
};

struct polymorphicT {  // bad: polymorphic
    int m1;
    int m2;
    virtual void foo();
};


struct inheritOkT: podT // ok: inheritance, data only on one level
{
    int f();
};


struct inheritPlusDataT: podT // bad: inheritance, data on 2 levels
{
    int m3;
};

template<typename T1, typename T2>
struct templT     // ok with std layout types T1, T2
{
    T1 m1;
    T2 m2;
};

// print type "name" and whether it's std layout
template<typename T>
void printIsStdLayout()
{
    printf("%-20s: %s\n", 
            typeid(T).name(),
            std::is_standard_layout<T>::value 
                ? "is std layout" 
                : "is NOT std layout");
}

int main()
{
    printIsStdLayout<funcOnlyT>();
    printIsStdLayout<podT>();
    printIsStdLayout<badAccessCtrlT>();
    printIsStdLayout<polymorphicT>();
    printIsStdLayout<inheritOkT>();
    printIsStdLayout<inheritPlusDataT>();
    printIsStdLayout<templT<int, float> >();
    printIsStdLayout<FILE>();
}

サンプルセッション:

$ g++ -std=c++11 -Wall -o isstdlayout isstdlayout.cpp && ./isstdlayout
9funcOnlyT          : is std layout
4podT               : is std layout
14badAccessCtrlT    : is NOT std layout
12polymorphicT      : is NOT std layout
10inheritOkT        : is std layout
16inheritPlusDataT  : is NOT std layout
6templTIifE         : is std layout
9__sFILE64          : is std layout
14
C++ 11§9/ 8 ([クラス]/8)

Astandard-layout structは、class-keystructで定義された標準レイアウトクラスです。またはclass-keyclassstandard-layout unionは、class-keyunionで定義された標準レイアウトクラスです。

C++ 11§9/ 10 ([クラス10)

APOD structは、単純クラスと標準レイアウトクラスの両方であり、非静的データメンバーを持たない非ユニオンクラスです。タイプが非POD構造体、非PODユニオン(またはそのようなタイプの配列)。 […]

POD structは標準レイアウトクラスであるため、standard-layout structのサブセットです。私の知る限り、これはC++標準のstructの最も一般的な意味です。そして、おそらくあなたが探しているのは、standard-layout structを識別することができる型特性または型特性のセットです。

そして、タイプ特性のリストを見ると、is_classおよびis_standard_layout。型が満たされた場合¹¹それは「構造体」です。または、より正確には、C++ 11§9/ 8で定義されているstandard-layout structです。


について

型特性に[classとstruct]の区別がない理由を知りたい

まあ、あります。それは is_standard_layout特性。


について

この違いをチェックするのは常に役に立たないのですか、それとも私が理解できない理由が他にありますか?

いいえ、この違いをチェックしても無駄ではありません。標準は、非常に実用的であるという理由で、standard-layoutを定義しています。標準自体が指摘しているように、

C++ 11§9/ 9 ([クラス]/9)

[注:標準レイアウトクラスは、他のプログラミング言語で記述されたコードとの通信に役立ちます。レイアウトは9.2で指定されています。


注:
¹is_class特性は、classまたはstructについては真ですが、unionについてはそうではありませんが、標準では「共用体はクラスである」と定義されています。つまり特性は一般的な用語よりも具体的です。