为什么没有 std::is_struct 类型特征?

Why is there not an std::is_struct type trait?

我看到为了检查类型 T 是否是 class 我可以使用:

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

它 returns 对于 class 和结构都是正确的。我知道在 C++ 中它们几乎是一样的,但我想知道为什么它们在类型特征上没有区别。是查这个差异总是没用,还是还有什么我不明白的原因?

无法区分像

这样的空定义的语义差异
class C {
public:

};

来自

struct S {

};

或类似的

class C {

};

struct S {
private:

};

除了 structclass 关键字外,没有可检测到的行为差异。另见 this Q&A.

注意:正如@KyleStrand所说,推导也需要显式访问说明符,所以S : private Base {};C : Base {};是等价的,和[=一样18=] 和 C : public Base {};,其中 S 是结构,C 是 class,Base 可以是其中之一。

它们是同一回事。唯一的区别(默认成员可见性)仅存在于编译时。 structclass.

之间没有任何区别

ETA: 你可能想要的是 std::is_pod,它会告诉你你的 class 是否是 "plain old data type"。许多关于这个问题的讨论和评论似乎表明,这正是那些认为应该有区别的人真正想要的。

It returns true for both classes and structs. I know that in C++ they are almost the same thing, but I'd like to know why there's not a distinction between them in the type trait.

不幸的是,这是 C++ 中的一个常见误解。有时它来自根本的误解,但有时它来自英语的歧义。它可能来自不准确的编译器诊断、写得不好的书、不正确的 SO 答案……

你可能读过这样的东西:

"There is no difference in C++ between a struct and a class except the default visibility of members and bases."

这段话可以在某种意义上被解释为误导,因为 identityequality 的概念在使用时很难区分"no difference".

这样的短语

事实上,C++自1985年以来就没有结构体了。它只有classes。

The kind of types that you declare with the keyword class and the keyword struct are classes。时期。关键字 struct 以及使用该关键字定义 class 时默认的可见性规则仅用于与 C 的向后兼容……但这是语法问题。它不会使结果类型实际上是不同的类型。

类型特征没有区别,因为字面上没有区别。

其他人已经正确指出,在C++中,关键字structclass除了成员可见性不同外,意义相同。

是否调用如此定义的聚合类型 "structs" 或 "classes" 或 "weiruewzewiruz" 由您决定。为了交流的利益,通常建议遵循既定的惯例,所以我建议不要 "weiruewzewiruz".

还建议使用语义差异作为选词指南。 struct 的使用更常见于没有很多内部逻辑和不变量的简单聚合数据;典型的用法是 struct point { float x; float y; };。这种类型在文献中通常称为 "structs" 或 "structures"。如果有人在 C++ 中使用 fprintf 将第一个参数称为 "pointer to a FILE struct",那就不足为奇了。 FILE 是 Scott Meyers 在 "More Effective C++",第 34 项:

中的含义的示例

It is safe to assume that a structure definition that compiles in both languages [C and C++ -p.a.s] is laid out the same way by both compilers.

关于自然语言,这个词的选择 "structure" 并非巧合:Meyers 正在谈论一种普通的旧数据聚合,它在两种语言中具有相同的语义,一直到位级别。

关于编程语言,所讨论的数据聚合的 C++ 定义是否使用关键字 structclass(带有 public 访问说明符)并不重要。 struct 可能是更自然的选择,因为聚合的 C++ 语义是 C 结构的语义。此外,使用 struct 使 C 和 C++ 源代码更容易共享一种类型定义。

C++ 标准在自然语言和编程语言中都使用 "struct" 和 "structure",不仅在互操作性的情况下:1.7/5: "A structure declared as",或 3.2/4 struct X; // declare X as a struct type。最有趣的是 9/8,为互操作标准奠定基础:

8 A standard-layout struct is a standard-layout class defined with the class-key struct or the class-key class. [...]

读这篇文章的人如何声称 C++ 中没有结构,这超出了我的理解范围。这显然不是编辑错误,因为术语 "struct" 和 "class" 明确设置为相互关联。


然而,比用词选择和品味问题更有趣的是明显的、可检验的差异。在什么情况下 C++ 聚合可以与 C struct 相媲美并与之兼容?也许这个问题是您问题的基础?报价中提到的标准布局是标准。它在 9/7 中有详细说明,基本上规定

  • 继承层次结构中只有一个 class 可以具有非静态数据成员定义(可能是因为标准不想指定在这种层次结构中不同级别定义的数据元素的顺序);
  • 不允许使用虚函数或虚基 classes(因为 运行 时间信息需要额外的实例数据);
  • 所有成员都具有相同的 "access control"(public,受保护的或私有的;可能是因为实现可以通过访问控制自由订购)。

标准然后说

9 [ Note: Standard-layout classes are useful for communicating with code written in other programming languages. Their layout is specified in 9.2.—end note ]

当然,用 C 编译的结构定义满足这些标准,因此 Scott Meyers 的断言。 stdio.h 中的 FILE 是一个突出的、不平凡的例子。请注意,该标准不提供保证,因为对象布局取决于实现,并且可能仅通过编译器选项进行更改。

一个class是否有标准布局可以用类型特征std::is_standard_layout<T>来测试。以下程序受 an example on 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
C++11 §9/8 ([class]/8):

A standard-layout struct is a standard-layout class defined with the class-key struct or the class-key class. A standard-layout union is a standard-layout class defined with the class-key union.

C++11 §9/10 ([class]/10):

A POD struct is a non-union class that is both a trivial class and a standard-layout class, and has no non-static data members of type non-POD struct, non-POD union (or array of such types). […]

由于 POD 结构 是标准布局 class,因此它是 标准布局结构 的子集。据我所知,这是 C++ 标准中 struct 最普遍的含义。因此,大概您正在寻找的是一个类型特征或一组类型特征,它们可以让您识别 标准布局结构 本身。

瞧,看看类型特征列表,有 is_classis_standard_layout。当一个类型满足 ¹两者时,它就是一个“结构”。或者更准确地说,它是一个 标准布局结构 ,由 C++11 §9/8 定义。


关于

I'd like to know why there's not a distinction between [class and struct] in the type trait

嗯,有。这就是 is_standard_layout 特质。


关于

Is it always useless to check this difference, or is there some more reason that I don't understand?

不,检查这个差异不是没有用的。该标准定义了 standard-layout 因为它非常实用。正如标准本身所指出的,

C++11 §9/9 ([class]/9):

[Note: Standard-layout classes are useful for communicating with code written in other programming languages. Their layout is specified in 9.2.—end note ]


备注:
¹ is_class 特性对于 classstruct 是正确的,但对于 union 则不是,即使标准定义“联合是 class ”。 IE。该特征比一般术语更具体。