[英]Puzzling introduction of temporary in `* const&` on older C++ compilers
在使用较旧的 C++ 编译器的平台上构建时,我注意到在其他地方运行良好的代码出现了意外行为。 我不确定这是否表明旧编译器存在错误,或者标准发生了一些变化,但我一直在使用 C++11 明确构建。
特别是,较旧的编译器似乎在我不希望它的情况下为* const&
使用临时,并且当它的堆栈帧被清理和覆盖时,该临时导致 SIGSEGV。
这是我从原始代码中提取 MWE 的最大努力。 我的问题围绕 class C
的构造函数:
#include <stdio.h>
struct A {
int* i;
};
class B {
public:
int* const& i_ptr_const_ref; // normally not public
B(int* const& i_ptr) : i_ptr_const_ref(i_ptr) {}
operator int* const& (void) const { return i_ptr_const_ref; }
};
int null_b = { 0 };
int* null_b_ptr = &null_b;
int* const& null_b_ptr_const_ref = null_b_ptr;
class C {
public:
B b;
// C(const A* a_ptr) : b(a_ptr ? a_ptr->i : null_b_ptr_const_ref) {} // this works
C(A* a_ptr) : b(a_ptr ? a_ptr->i : null_b_ptr_const_ref) {} // this fails
// C(A* a_ptr) : b(a_ptr ? (int* const&) a_ptr->i : null_b_ptr_const_ref) {} // this works
// C(A* a_ptr) : b(a_ptr->i) {} // this works
};
int main(void) {
A a;
A* a_ptr = &a;
a_ptr->i = (int*) 42;
C c(a_ptr);
printf("c.b.i_ptr_const_ref = %p\n", (void*) c.b.i_ptr_const_ref);
printf("c.b= %p\n", (void*) c.b);
printf("a_ptr->i= %p\n", (void*) a_ptr->i);
return 0;
}
我打印出来的值应该都匹配,但是在 5.1 之前的 GCC 编译器和 18 之前的 ICC 编译器上(我知道英特尔以与其他编译器的“bug-for-bug”兼容性而自豪),中间的显示了一个堆栈地址而不是期望值。 我能够尝试正确运行的所有版本的 Clang 和 ELLCC 编译器。
未注释的C
构造函数是我想要使用的构造函数,但它不能正常工作。 如果我将A* a_ptr
参数设为 const,我会在 MWE 中得到预期的结果,但在更大的代码库中我不能这样做。 如果我不使用?:
在初始化程序中,或者如果我在初始化程序中明确地将a_ptr->i
为int* const&
,我也会得到预期的结果,但我不明白为什么我必须这样做。
我原以为用int*
* 初始化int* const&
会很好,但我最好的猜测是?:
以某种方式混淆了编译器。 任何人都可以帮助我了解旧的 GCC 和 ICC 编译器在这里是否不正确,或者我是否误解了该语言?
这很明显是一个编译器错误:当然int*
类型的左值可以隐式转换为int* const&
,因此条件运算符的结果应该是一个左值。
The printf
output appears mysterious, but in fact is a straightforward consequence of the bug: even the first read of c.b.i_ptr_const_ref
(which is of course a reference that is transparently followed) is reading from the dead temporary, but it has yet被覆盖并且仍然包含ai
的副本。 在第一个printf
之后,那个 memory 已经被破坏并且恰好保存了一个堆栈地址。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.