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

问题描述 投票:54回答:5

我已经看到,为了检查类型T是否是我可以使用的类:

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

它对类和结构都返回true。我知道在C ++中它们几乎是一样的,但我想知道为什么在类型特征中它们之间没有区别。检查这种差异总是没用,还是有一些我不理解的理由?

c++ c++11 typetraits
5个回答
166
投票

它对类和结构都返回true。我知道在C ++中它们几乎是一样的,但我想知道为什么在类型特征中它们之间没有区别。

不幸的是,这是C ++中常见的误解。有时它来自于基本的误解,但有时它来自于英语的含糊不清。它可能来自不准确的编译器诊断,写得不好的书,不正确的SO答案......

你可能读过这样的东西:

“除了成员和基础的默认可见性之外,结构和类之间的C ++没有区别。”

这段经文可以被误解,因为在使用诸如“无差异”之类的短语时,很难区分身份和平等的概念。

事实上,自1985年以来,C ++还没有结构。它只有类。

The kind of types that you declare with the keyword class and the keyword struct are classes。期。关键字struct和使用该关键字定义类时默认的可见性规则只是为了与C ...向后兼容而保留,但这是一种语法。它不会使得到的类型实际上是另一种类型。

类型特征没有区别,因为字面意思不是一个要做的。


27
投票

对于像空的定义,不可能区分语义的差异

class C {
public:

};

struct S {

};

或类似的

class C {

};

struct S {
private:

};

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

注意:正如@KyleStrand所述,派生也需要显式访问说明符,所以S : private Base {};C : Base {};是等价的,与S : Base {};C : public Base {};相同,其中S是一个结构,C是一个类,Base可以是。


23
投票

他们是一回事。唯一的区别(默认成员可见性)仅在编译时存在。 structclass之间没有任何区别。

ETA:你可能想要的是std::is_pod,它会告诉你你的班级是否是“普通旧数据类型”。关于这个问题的大部分讨论和评论似乎表明,这就是那些认为应该有区别的人真正想要的。


14
投票

其他人正确地指出,在C ++中,关键字structclass具有相同的含义,除了成员可见性的差异。

您是否调用聚合类型,因此定义“结构”或“类”或“weiruewzewiruz”取决于您。为了沟通,通常建议遵循既定的惯例,因此我建议反对“weiruewzewiruz”。

还建议使用语义差异作为单词选择的指导。对于简单的聚合数据,struct的使用更为常见,它没有很多内部逻辑和不变量;典型的用途是struct point { float x; float y; };。这些类型在文献中通常称为“结构”或“结构”。如果有人在C ++中使用fprintf将第一个参数称为“指向FILE结构的指针”,那就不足为奇了。 FILE是Scott Meyers在“更有效的C ++”中的含义的一个例子,第34项:

可以安全地假设两种语言编译的结构定义[C和C ++ -p.a.s]由两个编译器以相同的方式排列。

关于自然语言,单词选择“结构”并非巧合:迈耶斯正在谈论一种普通的旧数据集合,它在两种语言中具有相同的语义,直到位级别。

关于编程语言,如果所讨论的数据聚合的C ++定义使用关键字structclass(使用公共访问说明符),则无关紧要。 struct可能是更自然的选择,因为聚合的C ++语义是C结构的语义。此外,使用struct可以使C和C ++源更容易共享一个类型定义。

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

标准布局结构是使用类键结构或类键类定义的标准布局类。 [...]

任何阅读此内容的人都可以声称C ++中没有任何结构是超出我的。这显然不是编辑错误,因为术语“struct”和“class”是相互明确设置的。


然而,比单词选择和品味问题更有趣的是明显的,可测试的差异。在什么情况下C ++聚合与C struct相当并兼容?也许这个问题是你的问题的根源?报价中提到的标准布局是标准。它在9/7中详细说明,并且基本上规定了这一点

  • 继承层次结构中只有一个类可能具有非静态数据成员定义(可能是因为标准不希望指定在这种层次结构中的不同级别定义的数据元素的顺序);
  • 不允许使用虚函数或虚基类(因为运行时信息需要额外的实例数据);
  • 所有成员都具有相同的“访问控制”(公共,受保护或私有;可能因为访问控制可以免费订购实现)。

然后标准说

9 [注意:标准布局类对于与使用其他编程语言编写的代码进行通信非常有用。它们的布局在9.2中指定.-尾注]

当然,在C中编译的结构定义符合这些标准,因此Scott Meyers的断言。来自stdio.h的FILE是一个突出的,不是非常重要的例子。请注意,标准不保证,因为对象布局是依赖于实现的,并且可能仅使用编译器选项进行更改。

是否具有标准布局的类可以使用类型特征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

7
投票
C++11 §9/8 ([class]/8):

“标准布局结构是使用类密钥struct或类密钥class定义的标准布局类。标准布局联合是使用类密钥union定义的标准布局类。

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

“POD结构是一个非联合类,它既是一个普通类,也是一个标准布局类,并且没有非POD结构,非POD联合(或这类类型的数组)类型的非静态数据成员。 [...]

由于POD结构是标准布局类,因此它是标准布局结构的子集。据我所知,这是C ++标准中struct的最一般含义。所以你想要的是一个类型特征或一组类型特征,它们可以让你识别标准布局结构。

而且,查看类型特征列表的是is_classis_standard_layout。当一种类型令人满意时,它就是一个“结构”。或者更确切地说,它是标准布局结构,由C ++11§9/ 8定义。


关于

“我想知道为什么类型特征中的[class和struct]之间没有区别

好吧,有。那就是is_standard_layout的特质。


关于

“检查这种差异总是没用,还是有一些我不理解的理由?

不,检查这种差异并非毫无用处。该标准定义了标准布局,因为它非常实用。正如标准本身所说,

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

“[注意:标准布局类对于与用其他编程语言编写的代码进行通信非常有用。它们的布局在9.2中指定.-尾注]


笔记: ¹is_class特性适用于classstruct,但不适用于union,尽管该标准定义“一个联盟是一个阶级”。即特征比一般术语更具体。

© www.soinside.com 2019 - 2024. All rights reserved.