我可以将 final 关键字应用于 C++11 中的 POD(标准布局)结构吗?我是不是该?

Can I apply the final keyword to a POD (standard-layout) struct in C++11 ? Should I?

在一个充满对象(具有正确行为)和相对较少的非面向对象结构(仅由数据字段和无方法组成)的 C++ 项目中,我想防止意外误用这些结构,其中可能会尝试创建一个继承自它的 class。

据我了解,因为这些 "POD"(普通旧数据)结构没有虚拟析构函数,所以无法正确删除派生的 class 对象(如果允许的话)通过 POD 类型的指针创建它。

这似乎是 C++11 "final" 关键字的一个很好的用例,它将 class 或结构标记为不可继承。

但是,我想知道 "final" 关键字是否导致结构成为非 POD?

我怀疑标准文档可能已经解决了这个问题,但我不够聪明,无法在很长的文档中进行筛选以找出答案。欢迎任何有用的指点。

注意:我对仅仅知道它通过了某些编译器供应商的编译不感兴趣。通过编译不保证:

#include <iostream>
using namespace std;

struct Pod final
{
    int a;
    int b;
    int c;
};

#if 0
class FailsBecauseCannotDeriveFromFinalAnything : public Pod
{
};
#endif

class ContainsSomethingFinalAsMember
{
public:
    ContainsSomethingFinalAsMember() : pod() {}
private:
    Pod pod;
};

int main() 
{
    std::cout << std::is_pod < Pod > :: value << std::endl;
    return 0;
}

According to my understanding, because these "POD" (plain old data) structs do not have a virtual destructor, it is not possible to properly delete a derived class object (if one is allowed to create it) via a pointer of the POD type.

给定原始指针是不可能的,但给定智能指针对象是可能的,例如 std::shared_ptr 或带有适当删除器的 std::unique_ptr

自从智能指针被标准化后,就没有理由继续遵循手动使用 delete 运算符的不良做法。当然,classes 不应围绕与 delete 运算符的兼容性而设计。每个 class 界面在设计时都应考虑到其特定用途。

不,让每个 class 成为 final 或多态性都不是一个好习惯。

However, I wonder if the "final" keyword causes the struct to become non-POD?

不,它仍然是 POD。 POD 的要求是标准布局(反过来不需要基数 class、成员之间没有访问说明符、没有虚拟任何东西)和简单的特殊成员函数(copy/move 构造函数、赋值运算符、析构函数)。

但是,POD-ness 的目的是允许您使用 memcpy 而不是正确构造对象,好的 C++ 代码会避免这样做。

标记 class final 不会更改其 POD 状态。引用 C++11:

9 [class]:

6 A trivially copyable class is a class that:

  • has no non-trivial copy constructors (12.8),
  • has no non-trivial move constructors (12.8),
  • has no non-trivial copy assignment operators (13.5.3, 12.8),
  • has no non-trivial move assignment operators (13.5.3, 12.8), and
  • has a trivial destructor (12.4).

A trivial class is a class that has a trivial default constructor (12.1) and is trivially copyable. [ Note: In particular, a trivially copyable or trivial class does not have virtual functions or virtual base classes.—end note ]

7 A standard-layout class is a class that:

  • has no non-static data members of type non-standard-layout class (or array of such types) or reference,
  • has no virtual functions (10.3) and no virtual base classes (10.1),
  • has the same access control (Clause 11) for all non-static data members,
  • has no non-standard-layout base classes,
  • either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
  • has no base classes of the same type as the first non-static data member.

8 A standard-layout struct is a standard-layout class defined with the class-key struct or the class-key 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). ...

12.1 [class.ctor]

5 ... A default constructor is trivial if it is not user-provided and if:

  • its class has no virtual functions (10.3) and no virtual base classes (10.1), and
  • no non-static data member of its class has a brace-or-equal-initializer, and
  • all the direct base classes of its class have trivial default constructors, and
  • for all the non-static data members of its class that are of class type (or array thereof), each such class has a trivial default constructor.

Otherwise, the default constructor is non-trivial.

12.4 [class.dtor]

5 ... A destructor is trivial if it is not user-provided and if:

  • the destructor is not virtual,
  • all of the direct base classes of its class have trivial destructors, and
  • for all of the non-static data members of its class that are of class type (or array thereof), each such class has a trivial destructor.

Otherwise, the destructor is non-trivial.

12.8 [class.copy]

12 A copy/move constructor for class X is trivial if it is not user-provided and if — class X has no virtual functions (10.3) and no virtual base classes (10.1), and — the constructor selected to copy/move each direct base class subobject is trivial, and — for each non-static data member of X that is of class type (or array thereof), the constructor selected to copy/move that member is trivial;

otherwise the copy/move constructor is non-trivial.

25 A copy/move assignment operator for class X is trivial if it is not user-provided and if

  • class X has no virtual functions (10.3) and no virtual base classes (10.1), and
  • the assignment operator selected to copy/move each direct base class subobject is trivial, and
  • for each non-static data member of X that is of class type (or array thereof), the assignment operator selected to copy/move that member is trivial;

otherwise the copy/move assignment operator is non-trivial.

如您所见,POD 结构的定义没有考虑 class 是否具有 class-virt-specifier final.