如何修复模拟std::any的自定义Any类以正确输出数据?
问题:自定义std::any实现的数据存储异常
尝试通过基类指针指向模板派生类的方式模拟std::any,实现存储任意类型数据的功能,但使用时无法正确存储数据,输出结果为非法值。
自定义Any类实现
class Any { TypeBase *_ptr; public: Any(): _ptr(nullptr) {} Any(const Any &other): Any() { _ptr = other._ptr->copy(); } Any(Any&& _other): Any() { _ptr = _other._ptr; _other._ptr = nullptr; } template<typename T> Any(T&& _data): Any() { /* * this function attempts to capture rvalue param */ _ptr = new Container<T>(std::forward<T>(_data)); } ~Any() { delete _ptr; } template<typename T> T& data_cast() const { if (_ptr == nullptr) throw std::runtime_error("Container empty"); return static_cast<Container<T>*>(_ptr)->data; } };
工具类定义
struct TypeBase { virtual ~TypeBase() {} virtual TypeBase* copy() = 0; }; template<typename T> struct Container: public TypeBase { T data; // those datas will store here Container() = delete; template<typename U> Container(U&& _data): data(std::forward<U>(_data)) {} virtual ~Container() {} virtual TypeBase* copy() override { return (new Container<T>(data)); } };
测试代码
#include <stdexcept> #include <iostream> // paste the definition given above int main() { double y = 13.32; Any a = y; std::cout << "The data of 'a' is: " << a.data_cast<double>() << std::endl; /* it doesn't work here, only outputs some wrong number */ return 0; }
输出结果:The data of a is: 3.10818e-317
问题分析与修复方案
核心错误原因
- 万能引用的类型推导错误:当传入左值
double y时,模板构造函数的T会被推导为double&(万能引用特性:左值传入时T为左值引用类型),导致Container<T>实际是Container<double&>,存储的是引用而非值。 - 类型转换不匹配:调用
data_cast<double>()时,代码将TypeBase*强制转换为Container<double>*,但实际指针指向的是Container<double&>对象,类型不匹配导致访问非法内存,输出垃圾值。
修复步骤
1. 修正模板构造函数,存储值类型而非引用
使用std::decay_t<T>去除类型的引用、cv限定符,确保Container存储的是值类型(符合std::any的行为),需要包含<type_traits>头文件。
修改Any类的模板构造函数:
template<typename T> Any(T&& _data): Any() { using DecayedType = std::decay_t<T>; _ptr = new Container<DecayedType>(std::forward<T>(_data)); }
2. 增强类型安全检查
在data_cast中使用dynamic_cast替代static_cast,如果类型不匹配则抛出std::bad_cast异常,避免非法内存访问:
修改data_cast函数:
#include <typeinfo> // 用于std::bad_cast template<typename T> T& data_cast() const { if (_ptr == nullptr) throw std::runtime_error("Container empty"); using TargetContainer = Container<std::decay_t<T>>; auto derived_ptr = dynamic_cast<TargetContainer*>(_ptr); if (!derived_ptr) throw std::bad_cast(); return derived_ptr->data; }
3. 补充缺失的头文件与完善值语义
测试代码中需要包含<iostream>才能使用std::cout;同时补充重载赋值运算符,完善类的拷贝/移动语义。
修复后的完整代码
#include <stdexcept> #include <iostream> #include <type_traits> #include <typeinfo> struct TypeBase { virtual ~TypeBase() {} virtual TypeBase* copy() = 0; }; template<typename T> struct Container: public TypeBase { T data; Container() = delete; template<typename U> Container(U&& _data): data(std::forward<U>(_data)) {} virtual ~Container() {} virtual TypeBase* copy() override { return new Container<T>(data); } }; class Any { TypeBase *_ptr; public: Any(): _ptr(nullptr) {} Any(const Any &other): Any() { if (other._ptr) { _ptr = other._ptr->copy(); } } Any(Any&& _other): Any() { _ptr = _other._ptr; _other._ptr = nullptr; } template<typename T> Any(T&& _data): Any() { using DecayedType = std::decay_t<T>; _ptr = new Container<DecayedType>(std::forward<T>(_data)); } ~Any() { delete _ptr; } // 重载赋值运算符 Any& operator=(const Any& other) { if (this != &other) { delete _ptr; _ptr = other._ptr ? other._ptr->copy() : nullptr; } return *this; } Any& operator=(Any&& other) noexcept { if (this != &other) { delete _ptr; _ptr = other._ptr; other._ptr = nullptr; } return *this; } template<typename T> T& data_cast() const { if (_ptr == nullptr) throw std::runtime_error("Container empty"); using TargetContainer = Container<std::decay_t<T>>; auto derived_ptr = dynamic_cast<TargetContainer*>(_ptr); if (!derived_ptr) throw std::bad_cast(); return derived_ptr->data; } }; int main() { double y = 13.32; Any a = y; std::cout << "The data of 'a' is: " << a.data_cast<double>() << std::endl; // 测试右值传入 Any b = 123; std::cout << "The data of 'b' is: " << b.data_cast<int>() << std::endl; // 测试拷贝构造 Any c = a; std::cout << "The data of 'c' is: " << c.data_cast<double>() << std::endl; return 0; }
输出结果
The data of 'a' is: 13.32 The data of 'b' is: 123 The data of 'c' is: 13.32
内容的提问来源于stack exchange,提问作者Konvt
相关产品推荐
相关产品推荐

