假设我们有以下定义的两种文字类型 A 和 B 。文字类型 B 可以在编译时从文字类型 A 初始化。第三个非文字类型 C 包含文字类型 B 作为成员。问题是,如果我们在编译时初始化 a 并将其传递给 C 的构造函数,则 b 在编译时初始化还有?由于 C 是非文字类型,因此在编译时不会初始化其成员 v 。但是, b 是否会发生部分编译时初始化?如果是这种情况,如果在 C 中使用 constexpr 成员函数,例如 f ,是否会在编译时评估?显然我们不能在结构 C 之外的编译时使用这样的函数,因为我们不能声明 C 的对象是 constexpr
struct A {
constexpr A(int num): num{num} {}
int num;
};
struct B {
constexpr B(A a): num{a.num} {}
int num;
};
template<typename T>
struct C {
C(A a, std::initializer_list<T> data): b{a}, v{data} {}
constexpr int f() const {
return b.num + 42;
}
B b;
std::vector<T> v;
};
int main() {
constexpr A a {4};
C<int> c {a, {5}};
}
答案 0 :(得分:0)
您对f
的声明是非法的,因为它是非文字类的非static
constexpr
成员。 GCC 6.1给出以下错误:
error: enclosing class of constexpr non-static member function 'int C::f() const' is not a literal type
note: 'C' is not literal because:
note: 'C' has a non-trivial destructor
因此,询问B
C
数据成员是否在编译时被评估是没有意义的,因为没有办法观察它何时被评估。
答案 1 :(得分:0)
作为一般规则,除非在编译时需要最终结果,否则对constexpr函数的任何调用都可能,但不必在编译时评估 。由于C
的构造函数在编译时不需要其B
参数,因此如果它想要提前评估转换或生成代码,则由编译器决定。
一般来说,我相信您会发现编译器在任何大于零的优化级别上都会内联并评估构造函数,以便简单地将4分配给C
实例中的适当位置并直接调用向量构造函数,如果它还没有内联它并展开初始化循环,基本上最终得到的代码看起来像这样:
int main() {
C c = UNINITIALIZED;
c.b.num = 4;
c.v._buffer = operator new(sizeof(int) * _MINIMUM_ELEMENTS);
c.v._capacity = _MINIMUM_ELEMENTS;
c.v._size = 1;
c.v._buffer[0] = 5;
}
找到答案的唯一方法是编译代码并让编译器发出它生成的程序集。