当前位置:  开发笔记 > 编程语言 > 正文

关于C++中类型惩罚的观点?

如何解决《关于C++中类型惩罚的观点?》经验,为你挑选了2个好方法。

我很好奇C++中类型惩罚指针/数组的约定.这是我目前的用例:

通过将其视为32位整数数组(我们知道它的总长度是4的倍数),然后将所有值相加并忽略溢出,计算二进制blob数据的简单32位校验和.

我希望这样的函数看起来像这样:

uint32_t compute_checksum(const char *data, size_t size)
{
    const uint32_t *udata = /* ??? */;
    uint32_t checksum = 0;
    for (size_t i = 0; i != size / 4; ++i)
        checksum += udata[i];
    return udata;
 }

现在我的问题是,您认为转换data为"最佳"的方式是udata什么?

C风格演员?

udata = (const uint32_t *)data

假设所有指针都是可转换的C++强制转换?

udata = reinterpret_cast(data)

C++在任意指针类型之间使用中间转换void*

udata = static_cast(static_cast(data))

通过工会铸造?

union {
    const uint32_t *udata;
    const char *cdata;
};
cdata = data;
// now use udata

我完全意识到这不是一个100%可移植的解决方案,但我只希望在一小部分平台上使用它,我知道它可以工作(即未对齐的内存访问和指针别名的编译器假设).你会推荐什么?



1> Adam Rosenfi..:

就C++标准而言,litb的答案是完全正确的,也是最便携的.无论是通过C风格的强制转换,还是强制转换const char *data为a const uint3_t *,都会违反严格的别名规则(请参阅了解严格别名).如果您使用完全优化进行编译,那么代码很可能不会正确.static_castreinterpret_cast

通过联合(例如litb my_reint)进行转换可能是最好的解决方案,尽管它在技术上违反了规则,如果您通过一个成员写入联合并通过另一个成员读取它,则会导致未定义的行为.但是,几乎所有编译器都支持这一点,并且它会产生预期的结果.如果您绝对希望符合标准100%,请使用位移方法.否则,我建议通过联盟进行投射,这可能会给你更好的表现.


我不认为这个特定的例子确实打破了严格的别名.char*是严格别名规则下的特殊情况 - char*可能永远不会被假定为指向其他类型的指针的别名.但在我的回答中,我仍然保持安全:与其他类似的案例不同,它不值得做char*.
onebyone,类型惩罚不是问题,它已经由工会解决了.但问题是工会成员的阅读,尽管我们之前没有写过.标准似乎并不禁止它.但这是我们不确定的问题:/
@SteveJessop,强制别名允许转换为const char*,但严格别名不允许转换为uint32_t*.http://en.cppreference.com/w/cpp/language/reinterpret_cast中的解释点5以T2为目标显示.下面的类型别名部分说T2必须是char或unsigned char.因此,严格别名确实禁止转换为unsigned int*.

2> Steve Jessop..:

忽略效率,为了简化代码我会做:

#include 
#include 
#include 

uint32_t compute_checksum(const char *data, size_t size) {
    std::vector intdata(size/sizeof(uint32_t));
    std::memcpy(&intdata[0], data, size);
    return std::accumulate(intdata.begin(), intdata.end(), 0);
}

我也喜欢litb的最后一个答案,即依次移动每个char的那个,除了因为char可能会被签名,我认为它需要一个额外的掩码:

checksum += ((data[i] && 0xFF) << shift[i % 4]);

当打字类型是一个潜在的问题时,我宁愿不输入双关语而不是安全地尝试这样做.如果您不首先创建任何不同类型的别名指针,那么您不必担心编译器可能对别名做什么,维护程序员也不会通过联合看到您的多个static_cast.

如果你不想分配这么多额外的内存,那么:

uint32_t compute_checksum(const char *data, size_t size) {
    uint32_t total = 0;
    for (size_t i = 0; i < size; i += sizeof(uint32_t)) {
        uint32_t thisone;
        std::memcpy(&thisone, &data[i], sizeof(uint32_t));
        total += thisone;
    }
    return total;
}

足够的优化将完全取消内存中的memcpy和额外的uint32_t变量,只需读取一个未对齐的整数值,无论以何种最有效的方式在您的平台上执行,直接从源数组中取出.我希望其他"严肃"的编译器也是如此.但是这个代码现在比litb更大了,所以没有什么可说的,除了我之外更容易变成一个与uint64_t一样好用的函数模板,并且我的工作作为本地字节序而不是选择一点-endian.

这当然不是完全便携的.它假定sizeof(uint32_t)字符的存储表示以我们想要的方式对应于uin32_t的存储表示.问题暗示了这一点,因为它表明一个人可以被"视为"另一个人.Endian-ness,char是否是8位,以及uint32_t是否使用其存储表示中的所有位显然可以侵入,但问题暗示它们不会.


但我同意,因为我提到memcpy是低成本的,它阻止矢量化的事实可能是某些应用程序的显示阻止.
推荐阅读
臭小子
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有