自定义std::any实现:模板拷贝与移动构造器重载问题
修复自定义Any类的构造器重载匹配问题
问题背景
我实现了一款支持可配置小值优化的自定义std::any(STL的std::any无此配置,默认值很小,例如GCC中仅为一个指针大小),代码如下:
#pragma once #include <cstddef> #include <cstring> #include <typeinfo> #include <new> #include <utility> template<std::size_t STORAGE_SIZE> class Any { typedef void (*CopyFunc)(void *dst, const void *src); typedef void (*MoveFunc)(void *dst, void *src); typedef void(*DestroyFunc)(void *val); struct Operations { const std::type_info &typeInfo; std::size_t size; std::size_t align; CopyFunc copy; MoveFunc move; DestroyFunc destroy; }; template<typename T> static inline constexpr Operations OPERATIONS = { .typeInfo = typeid(T), .size = ([]() { if constexpr (std::is_same_v<T, void>) { return 0; } else { return sizeof(T); } })(), .align = ([]() { if constexpr (std::is_same_v<T, void>) { return 0; } else { return alignof(T); } })(), .copy = ([]() { if constexpr (std::is_same_v<T, void> || std::is_trivially_copy_constructible_v<T>) { return nullptr; } else { return static_cast<CopyFunc>([](void *dst, const void *src) { new (dst) T(*reinterpret_cast<const T*>(src)); }); } })(), .move = ([]() { if constexpr (std::is_same_v<T, void> || std::is_trivially_move_constructible_v<T>) { return nullptr; } else { return static_cast<MoveFunc>([](void *dst, void *src) { new (dst) T(std::move(*reinterpret_cast<T*>(src))); }); } })(), .destroy = ([]() { if constexpr (std::is_same_v<T, void> || std::is_trivially_destructible_v<T>) { return nullptr; } else { return static_cast<DestroyFunc>([](void *arg) { reinterpret_cast<T*>(arg)->~T(); }); } })() }; std::size_t m_storage[(STORAGE_SIZE + sizeof(std::size_t) - 1) / sizeof(std::size_t)]; void *m_heapStorage = nullptr; const Operations *m_operations = &OPERATIONS<void>; public: Any() = default; template<typename T> Any(const T &value) { if constexpr (sizeof(T) > sizeof(m_storage) || alignof(T) > alignof(std::size_t)) { m_heapStorage = new T(value); } else { new (m_storage) T(value); } m_operations = &OPERATIONS<T>; } template<typename T> Any(T &&value) noexcept { if constexpr (sizeof(T) > sizeof(m_storage) || alignof(T) > alignof(std::size_t)) { m_heapStorage = new T(std::move(value)); } else { new (m_storage) T(std::move(value)); } m_operations = &OPERATIONS<T>; } template<std::size_t N> Any(const Any<N> &value) { if (value.m_heapStorage || value.m_operations->size > sizeof(m_storage)) { m_heapStorage = new (std::align_val_t(value.m_operations->align)) char[value.m_operations->size]; if (value.m_operations) { value.m_operations->copy(m_heapStorage, value.m_heapStorage ? value.m_heapStorage : value.m_storage); } else { std::memcpy( m_heapStorage, value.m_heapStorage ? value.m_heapStorage : value.m_storage, value.m_operations->size ); } } else { if (value.m_operations->copy) { value.m_operations->copy(m_storage, value.m_storage); } else { std::memcpy(m_storage, value.m_storage, value.m_operations->size); } } m_operations = value.m_operations; } template<std::size_t N> Any(Any<N> &&value) noexcept { m_operations = value.m_operations; if (value.m_heapStorage) { m_heapStorage = value.m_heapStorage; value.m_heapStorage = nullptr; value.m_operations = &OPERATIONS<void>; } else { if (value.m_operations->size > STORAGE_SIZE) { m_heapStorage = new (std::align_val_t(value.m_operations->align)) char[value.m_operations->size]; } if (m_operations->move) { m_operations->move(m_heapStorage ? m_heapStorage : m_storage, value.m_storage); } else { std::memcpy(m_heapStorage ? m_heapStorage : m_storage, value.m_storage, value.m_operations->size); } } } ~Any() { if (m_operations->destroy) { m_operations->destroy(m_heapStorage ? m_heapStorage : m_storage); } if (m_heapStorage) { delete static_cast<char*>(m_heapStorage); } } [[nodiscard]] const std::type_info &type() const { return m_operations->typeInfo; } [[nodiscard]] std::size_t size() const { return m_operations->size; } [[nodiscard]] std::size_t align() const { return m_operations->align; } [[nodiscard]] bool isHeapAllocated() const { return m_heapStorage != nullptr; } template<typename T> [[nodiscard]] const T *get() const { if (m_operations->typeInfo == typeid(T)) { return reinterpret_cast<const T*>(m_heapStorage ? m_heapStorage : m_storage); } else { return nullptr; } } template<typename T> [[nodiscard]] T *get() { if (m_operations->typeInfo == typeid(T)) { return reinterpret_cast<T*>(m_heapStorage ? m_heapStorage : m_storage); } else { return nullptr; } } void clear() { if (m_operations->destroy) { m_operations->destroy(m_heapStorage ? m_heapStorage : m_storage); } if (m_heapStorage) { delete static_cast<char*>(m_heapStorage); m_heapStorage = nullptr; } m_operations = &OPERATIONS<void>; } template<typename T, typename... Args> void emplace(Args &&...args) { clear(); if constexpr (sizeof(T) > sizeof(m_storage) || alignof(T) > alignof(std::size_t)) { m_heapStorage = new T(std::forward<Args...>(args)...); } else { new (m_storage) T(std::forward<Args...>(args)...); } m_operations = &OPERATIONS<T>; } template<std::size_t N> Any<STORAGE_SIZE> &operator=(const Any<N> &value) { clear(); if (value.m_heapStorage || value.m_operations->size > sizeof(m_storage)) { m_heapStorage = new (std::align_val_t(value.m_operations->align)) char[value.m_operations->size]; if (value.m_operations) { value.m_operations->copy(m_heapStorage, value.m_heapStorage ? value.m_heapStorage : value.m_storage); } else { std::memcpy( m_heapStorage, value.m_heapStorage ? value.m_heapStorage : value.m_storage, value.m_operations->size ); } } else { if (value.m_operations->copy) { value.m_operations->copy(m_storage, value.m_storage); } else { std::memcpy(m_storage, value.m_storage, value.m_operations->size); } } m_operations = value.m_operations; return *this; } template<std::size_t N> Any<STORAGE_SIZE> &operator=(Any<N> &&value) noexcept { clear(); m_operations = value.m_operations; if (value.m_heapStorage) { m_heapStorage = value.m_heapStorage; value.m_heapStorage = nullptr; value.m_operations = &OPERATIONS<void>; } else { if (value.m_operations->size > STORAGE_SIZE) { m_heapStorage = new (std::align_val_t(value.m_operations->align)) char[value.m_operations->size]; } if (m_operations->move) { m_operations->move(m_heapStorage ? m_heapStorage : m_storage, value.m_storage); } else { std::memcpy(m_heapStorage ? m_heapStorage : m_storage, value.m_storage, value.m_operations->size); } } return *this; } template<typename T> Any &operator=(T &&value) noexcept { using TT = typename std::decay<T>::type; emplace<TT>(std::forward<T>(value)); return *this; } };
但部分构造器存在重载匹配错误:
int x = 10; Any<64> v0 = x; // 错误 Any<64> v1 = 10; // 正常 Any<128> v2 = v1; // 错误
在这两种错误场景中,编译器均错误选择了template<typename T> Any(T &&value)构造器,而非第一个场景预期的template<typename T> Any(const T &value),以及第二个场景预期的template<std::size_t N> Any(const Any<N> &value)。赋值运算符在传入Any对象时也存在同类问题(传入普通值时正常)。
问题原因
核心问题是万能引用的匹配优先级过高:
- 对于
Any<64> v0 = x;,x是左值,但万能引用模板template<typename T> Any(T &&value)可以推导T为int&,经引用折叠后匹配int&,而const T&构造器需要给x添加const限定,编译器认为万能引用的匹配更优,因此选错构造器。 - 对于
Any<128> v2 = v1;,v1是Any<64>类型的左值,万能引用模板可以推导T为Any<64>&,匹配优先级高于专门针对Any类型的拷贝构造器模板。
修复方案
通过**SFINAE(替换失败并非错误)**机制,限制万能引用模板仅在T不是Any类型时参与重载匹配,同时确保普通左值优先匹配const T&构造器。
具体修改步骤
1. 添加必要头文件
在代码开头新增:
#include <type_traits>
2. 改造万能引用构造器
将原万能引用构造器替换为:
template<typename T, typename = std::enable_if_t< !std::is_same_v<std::decay_t<T>, Any> && !std::is_base_of_v<Any, std::decay_t<T>> >> Any(T &&value) noexcept { using TT = std::decay_t<T>; if constexpr (sizeof(TT) > sizeof(m_storage) || alignof(TT) > alignof(std::size_t)) { m_heapStorage = new TT(std::forward<T>(value)); } else { new (m_storage) TT(std::forward<T>(value)); } m_operations = &OPERATIONS<TT>; }
这里用std::decay_t<T>去掉引用和cv限定,判断T是否为Any类型,避免万能引用匹配Any对象;普通左值会因std::decay_t<T>为原类型,优先匹配const T&构造器。
3. 改造万能引用赋值运算符
将原万能引用赋值运算符替换为:
template<typename T, typename = std::enable_if_t< !std::is_same_v<std::decay_t<T>, Any> && !std::is_base_of_v<Any, std::decay_t<T>> >> Any &operator=(T &&value) noexcept { using TT = std::decay_t<T>; emplace<TT>(std::forward<T>(value)); return *this; }
4. 补充同类型拷贝/移动构造器(可选但规范)
为了兼容同大小Any的拷贝,添加非模板的拷贝/移动构造器:
Any(const Any &value) { if (value.m_heapStorage || value.m_operations->size > sizeof(m_storage)) { m_heapStorage = new (std::align_val_t(value.m_operations->align)) char[value.m_operations->size]; if (value.m_operations->copy) { value.m_operations->copy(m_heapStorage, value.m_heapStorage ? value.m_heapStorage : value.m_storage); } else { std::memcpy( m_heapStorage, value.m_heapStorage ? value.m_heapStorage : value.m_storage, value.m_operations->size ); } } else { if (value.m_operations->copy) { value.m_operations->copy(m_storage, value.m_storage); } else { std::memcpy(m_storage, value.m_storage, value.m_operations->size); } } m_operations = value.m_operations; } Any(Any &&value) noexcept { m_operations = value.m_operations; if (value.m_heapStorage) { m_heapStorage = value.m_heapStorage; value.m_heapStorage = nullptr; value.m_operations = &OPERATIONS<void>; } else { if (value.m_operations->size > STORAGE_SIZE) { m_heapStorage = new (std::align_val_t(value.m_operations->align)) char[value.m_operations->size]; } if (m_operations->move) { m_operations->move(m_heapStorage ? m_heapStorage : m_storage, value.m_storage); } else { std::memcpy(m_heapStorage ? m_heapStorage : m_storage, value.m_storage, value.m_operations->size); } } }
同时补充同类型赋值运算符:
Any &operator=(const Any &value) { clear(); if (value.m_heapStorage || value.m_operations->size > sizeof(m_storage)) { m_heapStorage = new (std::align_val_t(value.m_operations->align)) char[value.m_operations->size]; if (value.m_operations->copy) { value.m_operations->copy(m_heapStorage, value.m_heapStorage ? value.m_heapStorage : value.m_storage); } else { std::memcpy( m_heapStorage, value.m_heapStorage ? value.m_heapStorage : value.m_storage, value.m_operations->size ); } } else { if (value.m_operations->copy) { value.m_operations->copy(m_storage, value.m_storage); } else { std::memcpy(m_storage, value.m_storage, value.m_operations->size); } } m_operations = value.m_operations; return *this; } Any &operator=(Any &&value) noexcept { clear(); m_operations = value.m_operations; if (value.m_heapStorage) { m_heapStorage = value.m_heapStorage; value.m_heapStorage = nullptr; value.m_operations = &OPERATIONS<void>; } else { if (value.m_operations->size > STORAGE_SIZE) { m_heapStorage = new (std::align_val_t(value.m_operations->align)) char[value.m_operations->size]; } if (m_operations->move) { m_operations->move(m_heapStorage ? m_heapStorage : m_storage, value.m_storage); } else { std::memcpy(m_heapStorage ? m_heapStorage : m_storage, value.m_storage, value.m_operations->size); } } return *this; }
相关产品推荐
相关产品推荐

