是否提倡使用std::any?

发布时间:
2024-05-08 21:30
阅读量:
14

别用 std::any,用微软的 proxy。

刚接触 C++17 的时候我以为 std::any 是个什么高大上的黑魔法,然后发现好像没啥实际用处,而且性能差得一匹。

说回 std::any,你说它替代 void* 吧,它还真替代不了。不光运行时性能远低于用 void* 传参,我要转个基类类型它都没法转,要它何用?

不光没用,你要不小心用了还可能在背后给你惹一堆破事。不管编译器怎么优化,总有一些情况 std::any 绕不过申请堆内存。

因为 std::any 的一个对象就这么大,能塞的东西肯定是有限的,如果塞的东西比 std::any 大,那会被塞到哪里就可想而知了;而且不同平台 std::any 给的缓冲区大小还不一样(从 16 字节到 64 字节都有),性能根本不可控。

也能理解这些编译器厂商为什么不喜欢标准委员会了。

你说这个缓冲区给小点吧,稍大点的对象就得申请堆内存;给大点吧,一个 char 哪怕只有一个字节 sizeof(std::any) 也那么大。

其他异常、RTTI 什么的就不说了,一个个全是性能杀手。

什么?你的项目把异常和 RTTI 禁了?对不起,用不了。

后来我用过微软出的 proxy 之后(好像快进标准了),感觉不止 std::any,虚函数, std::function (C++11), std::move_only_function (C++23), std::copyable_function (C++26) 统统都是 C++ 走过的弯路。

proxy 可以一行就实现一个 std::any,而且缓冲区大小随便定(没指定就用默认的两个指针大小),而且还不强制要求用 RTTI。

PRO_DEF_FACADE(MyAny); pro::proxy<MyAny> obj = pro::make_proxy<MyAny>(123);

proxy 能做的事情比这多多了,虚函数能干的它都能干,虚函数干不了的它也能干。

实现个 std::functionstd::move_only_function什么的也是简简单单,GitHub 上有例子,还可以支持多种重载。

// 定义抽象 namespace spec { template <class... Overloads> PRO_DEF_FREE_DISPATCH(Call, std::invoke, Overloads...); template <class... Overloads> PRO_DEF_FACADE(MovableCallable, Call<Overloads...>); template <class... Overloads> PRO_DEF_FACADE(CopyableCallable, Call<Overloads...>, pro::copyable_ptr_constraints); } // namespace spec // MyFunction 实现了 std::function 的主要功能且支持任意多种重载 // MyMoveOnlyFunction 实现了 std::move_only_function but 的主要功能且支持任意多种重载 template <class... Overloads> using MyFunction = pro::proxy<spec::CopyableCallable<Overloads...>>; template <class... Overloads> using MyMoveOnlyFunction = pro::proxy<spec::MovableCallable<Overloads...>>; int main() { auto f = [](auto&&... v) { printf("f() called. Args: "); ((std::cout << v << ":" << typeid(decltype(v)).name() << ", "), ...); puts(""); }; MyFunction<void(int)> p0{&f}; p0(123); // 输出 "f() called. Args: 123:i," (assuming GCC) MyMoveOnlyFunction<void(), void(int), void(double)> p1{&f}; p1(); // 输出 "f() called. Args:" p1(456); // 输出 "f() called. Args: 456:i," p1(1.2); // 输出 "f() called. Args: 1.2:d," return 0; }

里面那个 pro::copyable_ptr_constraints 可以改缓冲区大小,甚至还能做编译时反射,回头看 std::any 就是个残废

END