实现简易std::any时赋值字符串字面量报错的原因及修复方法
错误原因解析
- 第一个报错触发逻辑:传入字符串字面量
"abc"时,完美转发模板的类型推导会将Tp推导为数组引用类型const char (&)[4],C++语法禁止对引用类型执行new操作,直接触发new cannot be applied to a reference type错误。 - 转换目标
char的来源:剥离引用层后,Tp对应的原生类型是长度为4的const char数组const char[4];对数组类型执行placement new时,编译器会尝试逐个构造数组元素,元素类型就是单个char。而数组作为实参传入构造逻辑时会自动退化为const char*指针,因此编译器提示无法将const char*转换为char。 - 原实现的隐藏缺陷:没有记录存储对象的实际类型、没有调用存储对象的析构函数,即使绕过编译错误,也会存在内存泄漏、类型不匹配取值时的未定义行为。
修复方案
核心修复逻辑是在模板参数推导后,用std::decay_t对类型做退化处理:剥离引用、cv限定符,将数组类型转换为对应指针类型,将函数类型转换为函数指针,从根源避免拿到引用、数组等无法直接构造的类型。
补充类型擦除逻辑实现自动析构、类型安全校验后,可运行代码如下:
#include <iostream> #include <type_traits> #include <memory> #include <typeinfo> class TinyAny { struct StorageBase { virtual ~StorageBase() = default; }; template <typename T> struct Storage : StorageBase { T value; template <typename U> Storage(U&& v) : value(std::forward<U>(v)) {} }; std::shared_ptr<StorageBase> data; public: TinyAny() = default; template <typename Tp, typename T = std::decay_t<Tp>> TinyAny(Tp&& val) : data(std::make_shared<Storage<T>>(std::forward<Tp>(val))) {} template <typename Tp, typename T = std::decay_t<Tp>> TinyAny& operator=(Tp&& val) { data = std::make_shared<Storage<T>>(std::forward<Tp>(val)); return *this; } template <typename Tp, typename T = std::decay_t<Tp>> Tp get() { auto* storage = dynamic_cast<Storage<T>*>(data.get()); if (!storage) { throw std::bad_cast(); } return storage->value; } }; int main() { TinyAny var; var = "abc"; std::cout << var.get<const char*>() << std::endl; var = 123; std::cout << var.get<int>() << std::endl; var = std::string("hello world"); std::cout << var.get<std::string>() << std::endl; return 0; }
- 上述实现额外补了默认构造函数,支持先声明再赋值的使用方式;通过基类虚析构保证存储对象正确释放,
dynamic_cast做类型校验避免非法类型转换。
内容的提问来源于stack exchange,提问作者BBing
相关产品推荐
相关产品推荐

