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

模板类初始化带自定义删除器的N个std::unique_ptr构造报错求助

问题原因
  • 声明顺序错误:ptr_type 类型别名定义在类的public区域,位置晚于私有构造函数的实现,构造函数中使用ptr_type时编译器尚未识别该类型,触发构造函数匹配失败错误。
  • 版本兼容问题:若使用C++20之前的标准,无捕获lambda类型不支持默认构造,部分编译器在批量初始化std::unique_ptr数组时,即使显式传入deleter也可能触发构造校验失败。
修复方案
  1. 调整Storage类的成员声明顺序,将公共类型别名移到类的最开头,保证构造函数使用ptr_type时该类型已被定义。
  2. 替换lambda deleter为显式定义的仿函数,兼容所有C++11及以上标准,避免lambda默认构造的兼容问题。
  3. 修正数组初始化的语法,保证折叠展开的初始化列表符合聚合初始化要求。

修正后完整代码

#include <memory>
#include <utility>
#include <sys/mman.h>
#include <unistd.h>

// 单独定义deleter仿函数,替代lambda,兼容所有C++版本
template<typename Tp>
struct PeripheralDeleter {
    void operator()(Tp* ptr) const {
        if (ptr) munmap(ptr, sizeof(Tp));
    }
};

template<typename Tp, std::size_t N>
class Storage
{
public:
    // 公共类型定义移到最前面
    using ptr_type = std::unique_ptr<Tp, PeripheralDeleter<Tp>>;
    ptr_type ptr[N];

    Storage() : Storage(std::make_index_sequence<N>{}) {}

private:
    template<size_t ... indices>
    explicit Storage(std::index_sequence<indices...> const&)
        // 修正初始化语法,直接构造每个unique_ptr实例
        : ptr{ptr_type(nullptr, PeripheralDeleter<Tp>{})...}
    {
        // 消除未使用参数的编译警告
        (void)std::initializer_list<size_t>{indices...};
    }
};

template<typename Tp, size_t nControllers = 1, size_t offset = 0>
static Tp* getPeripheralPtr(unsigned base_address, int idx = 0, int mem_fd = -1)
{
    // 边界校验,避免数组越界
    if (idx < 0 || idx >= nControllers) return nullptr;

    static Storage<Tp, nControllers> storage;

    if (!storage.ptr[idx])
    {
        // 计算当前控制器的物理地址
        unsigned phy_addr = base_address + idx * offset;
        long page_size = sysconf(_SC_PAGESIZE);
        // 按页对齐计算mmap参数
        off_t mmap_offset = phy_addr & ~(page_size - 1);
        size_t mmap_len = (phy_addr + sizeof(Tp) + page_size -1) & ~(page_size -1);
        // 此处mem_fd为/dev/mem的打开文件描述符,需提前以读写权限打开,进程需要root权限
        void* virtaddr = mmap(nullptr, mmap_len, PROT_READ | PROT_WRITE, MAP_SHARED, mem_fd, mmap_offset);
        if (virtaddr == MAP_FAILED) return nullptr;
        // 调整到寄存器实际的虚拟地址
        Tp* reg_addr = reinterpret_cast<Tp*>(reinterpret_cast<uintptr_t>(virtaddr) + (phy_addr & (page_size - 1)));
        storage.ptr[idx].reset(reg_addr);
    }

    return storage.ptr[idx].get();
}
注意事项
  • 调用getPeripheralPtr时,offset参数要传入实际的两个控制器物理地址的偏移量,比如PWM0基地址是0x10000,PWM1是0x10100,offset就传0x100。
  • mmap操作需要提前打开/dev/mem设备获取文件描述符,且进程需要root权限,否则会映射失败。

内容的提问来源于stack exchange,提问作者Quest

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 11:39:02