c++ - 'reinterpret_cast' 的有效和可移植使用?
问题描述
我们在工作中玩一些代码高尔夫。目的是保留签名to_upper
并将所有参数返回给上层。我的一位同事提出了这个 ~~ugly~~ 精彩的代码:
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
std::string operator+(std::string_view& a, int const& b) {
std::string res;
for (auto c : a) {
res += (c - b);
}
return (res);
}
struct Toto {
std::string data;
};
struct Result {
std::string a;
std::string b;
};
std::unique_ptr<Toto> to_upper(std::string_view input_a,
std::string_view input_b) {
auto* res = new Result;
res->a = (input_a + 32);
res->b = (input_b + 32);
auto* void_res = reinterpret_cast<void*>(res);
auto* toto_res = reinterpret_cast<Toto*>(void_res);
return std::unique_ptr<Toto>(toto_res);
}
int main() {
std::unique_ptr<Toto> unique_toto_res = to_upper("pizza", "ananas");
auto* toto_res = unique_toto_res.release();
auto* res = reinterpret_cast<Result*>(toto_res);
std::cout << res->a << std::endl;
std::cout << res->b << std::endl;
return 0;
}
reinterpret_cast
就可移植性和 UB 而言,这种使用是否合适?我们认为这没关系,因为我们只是在类型上欺骗编译器,但也许我们错过了一些东西。
解决方案
std::string operator+(std::string_view& a, int const& b)
它可能不是完全不允许的,但在全局命名空间中为标准类定义运算符重载只是要求违反 ODR。如果您使用任何库并且如果其他人都认为这很好,那么其他人也可能会定义该重载。所以,这是个坏主意。
auto* void_res = reinterpret_cast<void*>(res);
这是完全没有必要的。通过直接重新解释强制转换为Toto*
.
有效(和便携)
假设小写和大写相距 32 并不是可移植到所有字符编码的假设。对于超出范围的字符,该函数也无法正常工作a...z
。
现在关于主要问题。reinterpret_cast
指向另一个本身的指针(或引用)永远不会有 UB。这完全取决于您如何使用生成的指针(或引用)。
这个例子有点不稳定,而唯一指针拥有重新解释的指针,因为如果抛出异常,那么它将尝试删除它,这将导致 UB。但是我认为不能抛出异常,所以应该没问题。否则,您只需重新解释强制转换,标准明确定义了中间类型的对齐要求不比原始类型更严格的情况(适用于本示例)。
该程序确实泄漏内存。
推荐阅读
- bash - 如何将包含时间戳的列转换为 csv 文件中的纪元时间
- batch-file - 添加前导零以使每个文件名具有相同的长度
- javascript - VS2019 在保存或构建时编译 TS 的方式不同,我该如何解决这个问题?
- regex - 根据分隔符从字符串末尾选择字符串的一部分
- javascript - 如何从 pdf 文件生成器将文件下载到 Go 和 React 中的本地客户端
- laravel - 无法在 Laravel 中为模型类设置属性
- aws-api-gateway - 如何为新的 HTTP API 创建包含油门和配额限制的使用计划?
- html - 如何将runat服务器添加到cls文件
- node.js - Nodejs-PSQL-ILIKE 搜索
- html - 响应式 div 框