首页 > 解决方案 > '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 而言,这种使用是否合适?我们认为这没关系,因为我们只是在类型上欺骗编译器,但也许我们错过了一些东西。

标签: c++undefined-behaviorreinterpret-cast

解决方案


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。但是我认为不能抛出异常,所以应该没问题。否则,您只需重新解释强制转换,标准明确定义了中间类型的对齐要求不比原始类型更严格的情况(适用于本示例)。

该程序确实泄漏内存。


推荐阅读