我有以下代码似乎在GDB中表现异常,这取决于复制/移动构造函数是否默认。
#include <iostream>
#define CUSTOM 0
class Percentage
{
public:
using value_t = double;
Percentage() = default;
~Percentage() = default;
template <typename T>
Percentage(T) = delete;
Percentage(value_t value):
m_value(value)
{}
#if CUSTOM == 1
Percentage(const Percentage& p):
m_value(p.m_value)
{}
Percentage& operator=(const Percentage& p)
{
m_value = p.m_value;
return *this;
}
Percentage(Percentage&& p):
m_value(std::move(p.m_value))
{}
Percentage& operator=(Percentage&& p)
{
m_value = std::move(p.m_value);
return *this;
}
#else
Percentage(const Percentage&) = default;
Percentage& operator=(const Percentage&) = default;
Percentage(Percentage&&) = default;
Percentage& operator=(Percentage&&) = default;
#endif
friend std::ostream& operator<<(std::ostream& os, const Percentage& p)
{
return os << (p.m_value * 100.0) << '%';
}
private:
value_t m_value = 0.0;
};
struct test
{
Percentage m_p;
void set(const Percentage& v) { m_p = v; }
Percentage get() const { return m_p; }
};
int main()
{
test t;
std::cout << "Value 1: " << t.get() << std::endl;
t.set(42.0);
std::cout << "Value 2: " << t.get() << std::endl;
std::cout << "Breakpoint here" << std::endl;
}
我启动GDB,在main的最后一个cout上添加一个断点,然后运行“ p t.get()”,我希望它是42,但是根据宏CUSTOM的值,我会得到42(当CUSTOM是1)或0(当CUSTOM为0时)。
发生了什么事?这是编译器gdb中的错误吗?
OS: Fedora 26
Compiler: gcc 7.3.1
Flags: -fsanitize=address,leak -O0 -g3 -std=c++17
GDB 8.0.1-36
答案 0 :(得分:3)
通常,由于test :: get的结果是“纯右值”,如果编译器未绑定到左值(如Percentage &&),则可以跳过其初始化。因此,要查看纯右值的内容,应将其存储在Percentage &&变量中,该变量“物化” pr值并延长返回的临时项的寿命。
这两种情况之间的差异似乎存在于可执行文件中(与GDB无关):在两种情况下,从可执行文件的反汇编中可以看出“ test :: get”不同,而如果我们使用优化进行编译在(-O3)上生成的程序集是相同的。
情况0:
Percentage get() { return m_p; }
4009f0: 55 push %rbp
4009f1: 48 89 e5 mov %rsp,%rbp
4009f4: 48 89 7d f0 mov %rdi,-0x10(%rbp)
4009f8: 48 8b 7d f0 mov -0x10(%rbp),%rdi
4009fc: 48 8b 3f mov (%rdi),%rdi
4009ff: 48 89 7d f8 mov %rdi,-0x8(%rbp)
400a03: f2 0f 10 45 f8 movsd -0x8(%rbp),%xmm0
400a08: 5d pop %rbp
400a09: c3 retq
400a0a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
情况1:
Percentage get() { return m_p; }
4009f0: 55 push %rbp
4009f1: 48 89 e5 mov %rsp,%rbp
4009f4: 48 83 ec 10 sub $0x10,%rsp
4009f8: 48 89 f8 mov %rdi,%rax
4009fb: 48 89 75 f8 mov %rsi,-0x8(%rbp)
4009ff: 48 8b 75 f8 mov -0x8(%rbp),%rsi
400a03: 48 89 45 f0 mov %rax,-0x10(%rbp)
400a07: e8 54 00 00 00 callq 400a60 <_ZN10PercentageC2ERKS_>
400a0c: 48 8b 45 f0 mov -0x10(%rbp),%rax
400a10: 48 83 c4 10 add $0x10,%rsp
400a14: 5d pop %rbp
400a15: c3 retq
400a16: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
400a1d: 00 00 00
由于在这种情况下,您从GDB调用了test :: get,所以您不只是在内存中打印一个值,所以您正在执行上面的行。您会看到调用了Percentage的副本构造函数,并且test :: get的返回似乎在rax寄存器中,而在第一个代码段中似乎隐式了构造函数,并且返回值存储在浮点寄存器xmm0。 我不知道为什么会有这种差异(也许有人汇编专家可以添加一些见识),但是我怀疑这就是GDB感到困惑的原因。