You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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对象时也存在同类问题(传入普通值时正常)。


问题原因

核心问题是万能引用的匹配优先级过高:

  1. 对于Any<64> v0 = x;,x是左值,但万能引用模板template<typename T> Any(T &&value)可以推导T为int&,经引用折叠后匹配int&,而const T&构造器需要给x添加const限定,编译器认为万能引用的匹配更优,因此选错构造器。
  2. 对于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;
}
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 23:50:35