下面的代码是否格式正确,特别是关于别名规则?

问题描述 投票:0回答:2

下面的模板函数是序列生成器的一部分。我想出了以下基于联合的解决方案,而不是手动轮班,以使操作更加明确。它适用于所有经过测试的编译器。 Godbolt 链接.

然而,尽管在实践中工作,但我担心会违反别名规则,这意味着它可能在未来或在 GCC 和 CLANG 以外的其他编译器中不起作用。

严格按照C++标准:下面的代码是否格式正确?它会导致未定义的行为吗?

template <int BITS>
uint64_t flog2(uint64_t num) {
    constexpr uint64_t MAXNUM = (uint64_t(1) << BITS);    
    if (num < MAXNUM) return num; 
    union FP {
        double dbl;
        struct {
            uint64_t man: 52;
            uint32_t exp: 11;
            uint32_t sign: 1;
        };
        struct {
            uint64_t xman: 52-BITS;
            uint32_t xexp: 11+BITS;
            uint32_t xsgn: 1;
        };
    };
    FP fp;
    fp.dbl = num;
    fp.exp -= 1023-1+BITS;
    return fp.xexp;
}

谢谢!

c++ undefined-behavior unions strict-aliasing
2个回答
2
投票

首先,该程序在 ISO 标准 C++ 中的句法格式错误。匿名

struct
成员不是标准的 C++(与 C 相比)。它们是一个扩展。在 ISO 标准 C++ 中,
struct
必须通过该名称命名和访问。

我会在剩下的答案中忽略它,并假装您是通过这样的名称访问的。


从技术上讲,这不是别名违规,而是读取联合对象中非活动成员的未定义行为

fp.exp -= 1023-1+BITS;

类型对此并不重要(与别名相反)。联合体中始终最多只有一个活动成员,这将是最后一个显式创建或使用成员访问/赋值表达式编写的成员。在您的情况下,

fp.dbl = num;
表示
dbl
是活跃成员,并且是唯一可以读取的成员。

在访问联合的标准布局类类型成员的公共初始序列的标准中有一个例外,在这种情况下,可以访问非活动成员,就好像它是活动成员一样。但是,即使是您的两个

struct {
成员也只有
BITS == 0
.

的非空公共初始序列

然而,在实践中,编译器通常明确支持这种类型的双关语,可能已经在允许的情况下实现 C 兼容性。


当然,即使将所有这些放在一边,位域的布局和所涉及类型的表示完全是实现定义的,您不能指望它具有普遍的可移植性。


1
投票

union 成员那里读取不是最近写的是未定义的行为。

此外,位字段的布局是实现定义的。

因此,从严格的 C++ 标准来看,此代码调用未定义的行为(通过在编写

exp
后读取
dbl
)并依赖于实现定义的行为,假设位域布局对应于
double
浮动点表示(顺便说一下,这也是实现定义的)。

© www.soinside.com 2019 - 2024. All rights reserved.