C++:POD类型可以包含const非指针成员吗?

7
#include <iostream>

struct A {
  const int test_;
};

static_assert(std::is_pod<A>::value, "must be POD type");

int main()
{
    std::cout<<"Hello World";
    return 0;
}

关于Clang和GCC,std::is_pod<A>::valuetrue,而在ICC和MSVC上为false

如果将const int test_;替换为int test_;const int* test_,则它也可以在ICC和MSVC上通过。

标准规定如何?


1
VS认为这个类不是“平凡的”(std::is_trivial<A>::valuefalse,影响了std::is_pod)- 奇怪的是std::is_trivially_copyable<A>::value却是true - Ted Lyngmo
请注意,POD已被弃用为概念,std::is_pod自C++20也被弃用。 - François Andrieux
2
@TedLyngmo,trivial是比trivially copyable更严格的要求:一个trivial类是一个可以被平凡复制的类,并且具有一个或多个默认构造函数(15.1),这些构造函数都是平凡的或已删除的,其中至少有一个不是已删除的。 我认为A不是平凡的,因为没有默认构造函数可用。 - user7860670
@user7860670 是的,我引用了cppreference上的措辞 - 但是已经删除了那个引用。 - Ted Lyngmo
3
我认为“指针与否”的问题并不相关。const int * test_是一个非常量成员,但它所指向的东西恰好是const的。我敢打赌int* const test_;也会被ICC/MSVC视为非POD。 - Nate Eldredge
@NateEldredge,你是正确的。 - matthias_buehlmann
3个回答

4

N4659

12/10

POD结构体是一个既是平凡类又是标准布局类,且没有非POD struct、非POD union(或此类类型的数组)数据成员的非联合类。

对于平凡类:

12/6

平凡类是一个平凡可复制的类,且具有一个或多个默认构造函数。

这里没有可用的默认构造函数。

此外,对于平凡可复制性的要求包括:

12/6.2

至少具有一个未删除的复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符。

由于包含了const int,它不是平凡可复制的(没有赋值运算符),因此它不是POD结构体。

^ 我错了,它通过具有构造函数而是可复制的。我把“或”看成了“和”。


1
我认为你在这里走错了。A是平凡可复制的,因为只有一个复制构造函数就足以满足至少一个未删除的复制构造函数、移动构造函数、复制赋值运算符或移动赋值运算符。真正的问题是唯一的隐式声明默认构造函数被视为已删除。 - user7860670
@user7860670 你说得对,已经更新了,谢谢。 - Ryan Haining

2

POD类型的要求之一是必须是一个平凡类型。

[class]
10 一个POD结构体是一个非联合类,既是平凡类又是标准布局类,并且没有非POD结构体、非POD联合体(或这些类型的数组)的非静态数据成员。 其中平凡类型的定义如下:

而“平凡”在同一节中有定义:

6 平凡类是指可以平凡复制的类,具有一个或多个默认构造函数(15.1),所有这些构造函数都是平凡的或已删除的,并且至少有一个不被删除。

因为A具有const-qualifier的字段,需要强制进行初始化,其隐式声明的默认构造函数被删除。因此,A不是平凡类型也不是POD类型。所以VS和ICC在这里是正确的。不过,由于POD特性在C++20中已被弃用,并且应该避免使用先前的标准编写代码,所以这可能并不重要。


-2

您可以在此处找到is_pod测试的解释以及它不测试的内容:https://en.cppreference.com/w/cpp/types/is_pod。PODTypes的描述在此处:https://en.cppreference.com/w/cpp/named_req/PODType

要满足POD测试的要求,类型必须同时是:

平凡的和标准布局的

TrivialType的定义在此处:https://en.cppreference.com/w/cpp/named_req/TrivialType

标准布局类型在此处:https://en.cppreference.com/w/cpp/named_req/StandardLayoutType

TrivialType进一步定义了两个要求:

  • TriviallyCopyable(可平凡复制)
  • 如果类型是类类型或其数组,则该类具有一个或多个合格的默认构造函数,所有这些构造函数都是平凡的。

“可平凡复制”在此处定义:https://en.cppreference.com/w/cpp/named_req/TriviallyCopyable

基于此:http://www.cplusplus.com/reference/type_traits/is_trivially_copyable/Does a c++ struct have a default constructor?,很明显由于使用了隐式构造函数,结构体是可平凡复制的。另请参见:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf 第41页。

由于没有定义构造函数,因此存在隐式默认构造函数,因此满足平凡类型的两个要求

标准布局要求:

  • 所有非静态数据成员具有相同的访问控制
  • 没有虚函数或虚基类
  • 没有引用类型的非静态数据成员
  • 所有非静态数据成员和基类本身都是标准布局类型
  • 没有两个(可能是间接的)相同类型的基类子对象
  • 所有非静态数据成员和位字段在同一个类中声明(要么全部在派生类中,要么全部在某个基类中)
  • 任何基类子对象均不具有与之相同的类型:对于非联合类型,不具有第一个非静态数据成员(参见空基类优化),以及该数据成员的第一个非静态数据成员(如果它具有非联合类类型),或该数据成员的所有非静态数据成员(如果它具有联合类型),或该数据成员的元素(如果它具有数组类型)等,递归地。

因此它也具有标准布局。

因此,由于具有标准布局和平凡性,该结构体应被视为POD。


不,我的意思是没有默认构造函数,因为所有提到的实现都已经被隐式地“删除”了。 - Ted Lyngmo
那个问题没有一个const成员变量,所以默认构造函数没有被删除 - 因此不是同一件事情。 - Ted Lyngmo
1
同一份文件的第281页:“如果类X的默认构造函数是默认的,则在以下情况下,它将被定义为已删除:……任何非变量非静态数据成员属于const限定类型(或其数组),并且没有大括号或等于初始值的初始化器,也没有用户提供的默认构造函数”,因此,const int没有用户提供的默认构造函数。因此,默认构造函数被定义为已删除。 - Ted Lyngmo
1
C语言没有构造函数,而且相对更为灵活。在C语言中,即使是使用未初始化的const成员变量创建A对象,也是可以的。虽然我不确定这样做有什么好处。在C语言中,即使是读取未初始化的变量,也被认为是未定义的行为。 - Ted Lyngmo
1
@BeenishKhurshid gccclang 都会告诉你默认构造函数已被删除。 - Ryan Haining
显示剩余7条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接