#include <iostream>
#include <type_traits>
struct base_pod_t {
unsigned x;
};
struct der_pod_t : public base_pod_t { };
int main()
{
std::cout << "base_pod_t is POD: " << std::is_pod<base_pod_t>::value << std::endl;
std::cout << "der_pod_t is POD: " << std::is_pod<der_pod_t>::value << std::endl;
base_pod_t b1 = {}; // OK
base_pod_t b2 = {3}; // OK
der_pod_t p1 = {}; // OK
// der_pod_t p2 = {4}; // ERROR!
}
最后一行导致错误。如何用值来初始化der_pod_t
?
似乎即使它是POD它试图使用构造函数?
修改
由于@Praetorian和@dyb建议它是POD result of std::is_pod<der_pod_t>::value
is correct.
答案 0 :(得分:14)
base_pod_t
是一个聚合,您正在执行的初始化是聚合初始化。
来自§8.5.1[dcl.init.aggr]
1 聚合是一个数组或类(第9条),没有用户提供的构造函数(12.1),没有私有或受保护的非静态数据成员(第11条),没有基类(第10条),也没有虚函数(10.3)。
2 当聚合由初始化列表初始化时,如8.5.4中所述,初始化列表的元素被视为聚合成员的初始化者,增加下标或成员订购。每个成员都从相应的 initializer-clause 进行复制初始化。 ...
但是,der_pod_t
不是聚合,因为它有一个基类。这是一个POD,列表初始化的相同规则不适用。现在,当编译器看到一个非空的 braced-init-list 时,它将首先搜索一个带有initializer_list
的构造函数。如果没有找到它,则尝试匹配该类的其他构造函数。由于der_pod_t
没有构造函数将单个int
作为参数,因此会发生错误。
答案 1 :(得分:4)
从CPP 17开始,这是允许的,但要稍作改动,您需要为每个基类在初始化器列表中添加其他{}。请注意,在下面的示例中,{1,2}如何包含在“ {}”中并初始化i,j,而“ 3”则初始化派生的k。
struct base_pod
{
int i, j;
};
struct der_pod : public base_pod
{
int k;
};
der_pod dp{ {1 , 2}, 3 };
这适用于GCC 7.3.0版(不确定较早的版本),但在VS17(v 15.9.4)和带有“ / std:c ++ 17”标志的VS17上失败,因此注意您的编译器支持/标志。
答案 2 :(得分:0)
我今天一直在处理这个问题,并找到了解决方案,但我不能强调这个解决方案有多危险(见下文为什么危险)。
我的特殊问题是我只想用我自己的一些方法扩展库结构。我想保持POD与基本完全相同的布局,因为我想使用以base为参数的函数。
解决方案是这样的:
#include <iostream>
using namespace std;
struct BASE {
int x, y;
};
struct FOO: BASE {
void Foo() { x = y = 1; }
};
int main() {
// const declaration
const BASE a = { 0, 1 };
const FOO &b = *reinterpret_cast<const FOO *> (&a);
// non-const declaration
BASE _a = { 0, 3 };
FOO &c = *reinterpret_cast<FOO *> (&_a);
cout << "base: " << a.x << ", " << a.y << endl;
cout << "foo 1: " << b.x << ", " << b.y << endl;
cout << "foo 2: " << c.x << ", " << c.y << endl;
return 0;
}
但请注意,这只能起作用,因为BASE和FOO之间的数据布局是相同的。也只是因为我使用指针强制转换为FOO类型。在这种情况下,类型转换在没有任何构造函数的情况下完成,它只是假装内存格式正确。如果你尝试在没有指针的情况下reinterpret_cast,编译器将尝试基于原始构造一个新对象。
有关更好的说明,请参阅this answer。
不幸的是,对于这一点来说,似乎并不是一个很好的单线。适当的声明宏似乎是有序的。
答案 3 :(得分:0)
尝试一下;
struct A {
float data;
A() = default;
A(float d) : data{d} {}
};
struct B : A {
using A::A;
};
测试:
A aa{1}; // OK
B bb{1}; // OK
std::cout << std::is_pod<A>::value << std::endl; // output 1
std::cout << std::is_pod<B>::value << std::endl; // output 1
输出将显示A和B均为POD。
https://en.cppreference.com/w/cpp/named_req/TrivialType仅说:
具有一个或多个默认构造函数,所有这些构造函数都是微不足道的或已删除,并且至少有一个未删除。
它不允许自定义构造函数。