【发布时间】:2021-12-06 07:35:58
【问题描述】:
因此,我最近开始了一场运动,以清除在 GCC (-Wall -Wextra -Wpedantic) 中启用所有编译器警告时收到的一些警告,但我收到了一个关于未初始化值的相当混乱的警告。这是正在发生的事情的一个(稍微简化的)视图:
#include <cstdint>
#include <iostream>
constexpr uint64_t MASK = 0xFFFFFFFFFFFFFFFF;
double do_something(const double& rhs) {
double tmp(rhs);
reinterpret_cast<uint64_t&>(tmp) &= MASK;
return tmp;
}
int main() {
std::cout << do_something(3.14159);
}
编译
g++ -std=c++17 -O3 -Wall -Wextra -Wpedantic
基本上我将位掩码MASK 应用于双精度值,然后对原始数据和掩码数据执行一些其他操作。另一个操作涉及与此处无关的额外状态,并且位掩码存储在其他位置但已正确定义。奇怪的是,我得到了以下编译器警告
<source>: In function 'double do_something(const double&)':
<source>:8:32: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
8 | reinterpret_cast<uint64_t&>(tmp) &= MASK;
| ^~~
<source>:8:37: warning: 'tmp' is used uninitialized [-Wuninitialized]
8 | reinterpret_cast<uint64_t&>(tmp) &= MASK;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~
<source>:7:11: note: 'tmp' declared here
7 | double tmp(rhs);
尽管警告的上下文指向重新解释数据的 &= 操作。我认为这是因为编译器无法通过 reinterpret cast 看到在 (reinterpreted) 对 uint64_t 的引用中保存的上述行上初始化的有效数据。它是否正确?如果没有,谁能启发我?
免责声明:我知道你们中的一些人会因重新解释演员而被推迟,但这对于这段特定代码的运行方式至关重要,并且(除了警告之外)它在这种情况下是一个“安全”的操作。
【问题讨论】:
-
无法复制,请发minimal reproducible example。
-
reinterpret_cast<uint64_t&>(tmp) &= MASK;看起来像 UB。您正在读取和写入变量,就好像它不是一样的东西。您使用的是什么编译器和选项?也许 UB 让它做了一些你没想到的事情,导致tmp未初始化? -
memcpy是要走的路(或自 C++20 起为std::bit_cast)。否则你会违反严格的别名规则。 -
感谢您添加示例。此代码来自一个成熟的库,该库已在所有平台和所有主要编译器(GCC、Clang、MSVC、Intel)上进行了详尽的测试。转换为与原始值大小相同的整数类型不应该是未定义的行为,尽管像整数一样对其进行操作肯定不是有效的双精度操作,但这不是重点。
-
@SamM "...转换为与原始值大小相同的整数类型不应是未定义的行为..." - 它由 C++ 标准定义为未定义行为阅读此处的类型别名部分 - en.cppreference.com/w/cpp/language/reinterpret_cast
标签: c++ gcc gcc-warning