文字类型成员的编译时评估

Compile time evaluation of literal type members

本文关键字:评估 编译 类型 成员 文字      更新时间:2023-10-16

假设我们有两个文字类型AB,定义如下。文字类型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}};
}

您对f的声明是非法的,因为它是非文字类的非staticconstexpr成员。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

因此,询问CB数据成员是否在编译时被评估是没有意义的,因为当它被评估时没有办法观察。

一般来说,除非在编译时需要最终结果,否则对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;
}

唯一的方法是编译代码并让编译器发出它生成的程序集。