# C++ Weekly - Episode 159 脱水版:
constexpr
virtual` Members In C++20
C++20 中的
constexpr
,virtual
在 C++17 中,virtual
函数无法被同时声明为 constexpr
,
error: virtual function cannot be constexpr
但是在 C++20 中,可以。这意味着对于虚函数重载也可以实现编译期实现,从而提升运行效率。参见下述一个例子:
struct Base
{
[[nodiscard]] constexpr virtual int get_value() const = 0;
};
struct Derived: Base
{
[[nodiscard]] constexpr int get_value() const override {
return 5;
}
};
struct Derived2: Base
{
[[nodiscard]] constexpr int get_value() const override {
return 10;
}
};
constexpr auto get_some_values()
{
const Derived d1;
const Derived d2;
const Derived d3;
const Derived2 d4;
const Derived2 d5;
const std::array<const Base *, 5> data{ &d1, &d2, &d3, &d4, &d5};
int sum = 0;
for (const auto *elem : data) {
sum += elem->get_value();
}
return sum;
}
int main()
{
constexpr auto v = get_some_values();
return v;
}
在 gcc std=c++20 -O3
编译条件下, get_some_values()
直接输出了结果:
main:
mov eax, 35
ret
若使用非 constexpr
表达式,让其在运行时重载,源码及汇编后代码如下:
struct Base
{
[[nodiscard]] /* constexpr */ virtual int get_value() const = 0;
};
struct Derived: Base
{
[[nodiscard]] /* constexpr */ int get_value() const override {
return 5;
}
};
struct Derived2: Base
{
[[nodiscard]] /* constexpr */ int get_value() const override {
return 10;
}
};
/* constexpr */ auto get_some_values()
{
const Derived d1;
const Derived d2;
const Derived d3;
const Derived2 d4;
const Derived2 d5;
const std::array<const Base *, 5> data{ &d1, &d2, &d3, &d4, &d5};
int sum = 0;
for (const auto *elem : data) {
sum += elem->get_value();
}
return sum;
}
int main()
{
/* constexpr */ auto v = get_some_values();
return v;
}
Derived::get_value() const:
mov eax, 5
ret
Derived2::get_value() const:
mov eax, 10
ret
get_some_values():
push r12
push rbp
xor ebp, ebp
push rbx
sub rsp, 96
lea rax, [rsp+8]
lea rbx, [rsp+48]
mov QWORD PTR [rsp+8], OFFSET FLAT:vtable for Derived+16
movq xmm0, rax
lea rax, [rsp+16]
mov QWORD PTR [rsp+16], OFFSET FLAT:vtable for Derived+16
lea r12, [rsp+88]
mov QWORD PTR [rsp+24], OFFSET FLAT:vtable for Derived+16
movq xmm1, rax
lea rax, [rsp+24]
mov QWORD PTR [rsp+32], OFFSET FLAT:vtable for Derived2+16
punpcklqdq xmm0, xmm1
movaps XMMWORD PTR [rsp+48], xmm0
movq xmm0, rax
lea rax, [rsp+32]
mov QWORD PTR [rsp+40], OFFSET FLAT:vtable for Derived2+16
movq xmm2, rax
lea rax, [rsp+40]
punpcklqdq xmm0, xmm2
mov QWORD PTR [rsp+80], rax
mov eax, OFFSET FLAT:Derived::get_value() const
movaps XMMWORD PTR [rsp+64], xmm0
jmp .L6
.L8:
mov rax, QWORD PTR [rbx]
mov rax, QWORD PTR [rax]
mov rax, QWORD PTR [rax]
.L6:
mov rdi, QWORD PTR [rbx]
add rbx, 8
call rax
add ebp, eax
cmp rbx, r12
jne .L8
add rsp, 96
mov eax, ebp
pop rbx
pop rbp
pop r12
ret
main:
jmp get_some_values()
typeinfo name for Base:
.string "4Base"
typeinfo for Base:
.quad vtable for __cxxabiv1::__class_type_info+16
.quad typeinfo name for Base
typeinfo name for Derived:
.string "7Derived"
typeinfo for Derived:
.quad vtable for __cxxabiv1::__si_class_type_info+16
.quad typeinfo name for Derived
.quad typeinfo for Base
typeinfo name for Derived2:
.string "8Derived2"
typeinfo for Derived2:
.quad vtable for __cxxabiv1::__si_class_type_info+16
.quad typeinfo name for Derived2
.quad typeinfo for Base
vtable for Derived:
.quad 0
.quad typeinfo for Derived
.quad Derived::get_value() const
vtable for Derived2:
.quad 0
.quad typeinfo for Derived2
.quad Derived2::get_value() const
结果显而易见。