void*在现代C++中是危险信号,因它放弃编译期类型检查,易导致未定义行为;应优先用std::any(运行时类型安全)、std::variant(编译期已知类型)或模板(零开销泛型)。

void\* 为什么在现代 C++ 中被视为危险信号
因为 void\* 完全放弃编译期类型检查,把类型转换责任推给程序员,而现代 C++ 的核心设计哲学之一就是「让错误在编译时暴露」。一旦你用 void\* 传参、存储或返回,static_cast 或 reinterpret_cast 的时机、目标类型是否匹配、对象生命周期是否有效,全靠人工保证——这正是内存越界、未定义行为(UB)和难以调试崩溃的温床。
std::any 是最直接的类型安全替代方案
当你需要一个能存任意可复制类型的容器(比如配置项、插件参数、反射字段值),std::any 提供运行时类型信息 + 安全取值机制,比 void\* + 手动 reinterpret_cast 可靠得多:
std::any data = 42;
data = std::string("hello");
if (data.type() == typeid(std::string)) {
std::string& s = std::any_cast(data); // 安全:失败抛 std::bad_any_cast
}
-
std::any自动管理所含对象的构造/析构和内存 - 取值必须显式指定目标类型,不匹配时抛异常而非静默 UB
- 不能隐式转换,杜绝「我以为是 int,其实是 double」这类误用
- 注意:它有小对象优化(SOO),但大对象仍涉及堆分配,性能敏感路径需权衡
std::variant 更适合已知有限类型的场景
如果你实际只会在几个固定类型间切换(例如解析 JSON 值:int / double / bool / std::string / nullptr_t),std::variant 是零开销、类型安全、且支持 std::visit 的首选:
using json_value = std::variant; json_value v = 3.14; std::visit([](const auto& x) { using T = std::decay_t
; if constexpr (std::is_same_v ) { std::cout << "double: " << x << "\n"; } }, v);
- 编译期确定所有可能类型,无运行时类型擦除开销
- 访问必须覆盖全部分支(否则编译警告),强制处理每种情况
- 不能存非
nothrow_copy_constructible类型(如某些自定义类),这点比std::any严格 - 别用
std::get直接强取——先用(v) std::holds_alternative检查,否则抛(v) std::bad_variant_access
模板和概念(concepts)才是真正的“泛型”解法
绝大多数本想用 void\* 实现的通用逻辑(比如容器、算法、回调),其实应该用模板。C++20 的 concepts 还能让约束更清晰:
立即学习“C++免费学习笔记(深入)”;
templaterequires std::copyable && std::equality_comparable bool contains(const std::vector & v, const T& x) { return std::find(v.begin(), v.end(), x) != v.end(); }
- 编译器生成特化版本,无运行时开销,类型安全拉满
- 错误信息明确指向哪个约束不满足(比如传了不可拷贝的类型),而不是
void\*那种「段错误后才猜」 - 函数指针回调场景?用
std::function或模板参数,别用void\* user_data - 唯一合理用
void\*的地方:对接 C API(如qsort、OpenGL 回调)。此时务必用static_cast且确保生命周期严格对齐
真正难的不是选 std::any 还是 std::variant,而是识别出「这个接口真需要类型擦除吗」——多数时候,答案是否定的。强行用 void\* 省事,最后花十倍时间 debug,得不偿失。









