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

如何优化模板类中不同容器元素的自定义ID类型处理,消除重复声明?

问题描述

需要为存储自定义ID的类Dummy实现以下需求:

  • 输入数据可来自任意容器(如std::vector<int>、C数组等),每条记录类型为TD
  • 从记录中推导得到类型为TI的ID,存储在Dummy内部的std::vector<IdType>中
  • 避免不必要的拷贝,不允许用户先创建ID容器再传入构造函数

当前实现通过传递迭代器和转换回调函数完成,但存在模板参数推导失效的问题:必须同时在Dummy<IdType>和回调函数中重复指定ID类型(比如Dummy<std::string>与回调返回的std::string、Dummy<uint32_t>与回调里的static_cast<uint32_t>),需要消除这种重复声明的冗余。

当前代码如下:

#include <string>
#include <vector>   

template<typename IdType>
struct Dummy {
    template<typename InputIt, typename Converter>
    Dummy(InputIt first, InputIt last, Converter convert) {
        for (auto it = first; it != last; ++it) {
            _ids.push_back(convert(*it));
        }
    }
    std::vector<IdType> _ids;
};

int main() {
    std::vector<int> c1{1, 2, 3, 4, 5};
    auto d1 = Dummy<std::string>(c1.begin(), c1.end(), [](const auto& c) { return std::to_string(c); });

    std::pair<double, double> c2[2] = {{1.0, 2.0}, {3.0, 4.0}};
    auto d2 = Dummy<uint32_t>(c2, c2 + 2, [](const auto& c) { return static_cast<uint32_t>(c.first * c.second); });
}
解决方案

方法1:使用类模板推导指引(C++17及以上)

利用C++17的类模板推导指引,让编译器从回调函数的返回值自动推导IdType,无需手动指定。

修改后代码:

#include <string>
#include <vector>
#include <utility> // 用于std::declval

template<typename IdType>
struct Dummy {
    template<typename InputIt, typename Converter>
    Dummy(InputIt first, InputIt last, Converter convert) {
        _ids.reserve(std::distance(first, last)); // 提前预留空间,优化扩容性能
        for (auto it = first; it != last; ++it) {
            _ids.push_back(convert(*it));
        }
    }
    std::vector<IdType> _ids;
};

// 类模板推导指引:从回调的返回值推导IdType
template<typename InputIt, typename Converter>
Dummy(InputIt, InputIt, Converter) -> Dummy<std::decay_t<decltype(std::declval<Converter>()(*std::declval<InputIt>()))>>;

int main() {
    std::vector<int> c1{1, 2, 3, 4, 5};
    // 无需指定Dummy<std::string>,编译器自动推导IdType为std::string
    auto d1 = Dummy(c1.begin(), c1.end(), [](const auto& c) { return std::to_string(c); });

    std::pair<double, double> c2[2] = {{1.0, 2.0}, {3.0, 4.0}};
    // 自动推导IdType为uint32_t
    auto d2 = Dummy(c2, c2 + 2, [](const auto& c) { return static_cast<uint32_t>(c.first * c.second); });
}

说明:

  • 推导指引通过std::declval模拟调用回调函数,获取返回值类型后用std::decay_t去除引用和cv限定符,得到最终的IdType
  • 添加_ids.reserve提前预留空间,避免容器多次扩容,进一步优化性能

方法2:提供工厂函数(兼容C++11及以上)

如果需要兼容C++11/14,可通过工厂函数封装构造逻辑,让函数模板推导返回值类型,间接推导Dummy的模板参数。

代码示例:

#include <string>
#include <vector>
#include <utility>

template<typename IdType>
struct Dummy {
    template<typename InputIt, typename Converter>
    Dummy(InputIt first, InputIt last, Converter convert) {
        _ids.reserve(std::distance(first, last));
        for (auto it = first; it != last; ++it) {
            _ids.push_back(convert(*it));
        }
    }
    std::vector<IdType> _ids;
};

// 工厂函数:自动推导回调返回值作为IdType
template<typename InputIt, typename Converter>
auto make_dummy(InputIt first, InputIt last, Converter convert) -> Dummy<std::decay_t<decltype(convert(*first))>> {
    return Dummy<std::decay_t<decltype(convert(*first))>>(first, last, convert);
}

int main() {
    std::vector<int> c1{1, 2, 3, 4, 5};
    auto d1 = make_dummy(c1.begin(), c1.end(), [](const auto& c) { return std::to_string(c); });

    std::pair<double, double> c2[2] = {{1.0, 2.0}, {3.0, 4.0}};
    auto d2 = make_dummy(c2, c2 + 2, [](const auto& c) { return static_cast<uint32_t>(c.first * c.second); });
}

说明:

  • 工厂函数make_dummy通过decltype(convert(*first))获取回调返回值类型,经std::decay_t处理后作为Dummy的模板参数
  • 调用时只需写make_dummy,无需手动指定IdType,彻底消除重复声明

方法3:改进构造函数模板(兼容显式/自动推导)

将IdType也作为构造函数的模板参数,配合默认参数实现自动推导,同时保留显式指定IdType的能力:

#include <string>
#include <vector>

template<typename IdType>
struct Dummy {
    std::vector<IdType> _ids;

    // 构造函数模板:默认使用类模板的IdType,支持自动推导
    template<typename InputIt, typename Converter, typename T = IdType>
    Dummy(InputIt first, InputIt last, Converter convert, T* = nullptr) {
        _ids.reserve(std::distance(first, last));
        for (auto it = first; it != last; ++it) {
            _ids.push_back(static_cast<T>(convert(*it)));
        }
    }
};

// 可选推导指引:用于完全自动推导场景
template<typename InputIt, typename Converter>
Dummy(InputIt, InputIt, Converter) -> Dummy<std::decay_t<decltype(std::declval<Converter>()(*std::declval<InputIt>()))>>;

int main() {
    std::vector<int> c1{1, 2, 3, 4, 5};
    auto d1 = Dummy(c1.begin(), c1.end(), [](const auto& c) { return std::to_string(c); });

    std::pair<double, double> c2[2] = {{1.0, 2.0}, {3.0, 4.0}};
    // 如需强制指定uint32_t,可显式写Dummy<uint32_t>
    auto d2 = Dummy<uint32_t>(c2, c2 + 2, [](const auto& c) { return c.first * c.second; });
}

说明:

  • 构造函数模板的默认参数T = IdType,当显式指定Dummy<IdType>时,T为指定类型;当自动推导时,T由推导指引确定
  • 若需要强制转换到特定IdType,仍可显式指定Dummy<uint32_t>,构造函数会自动将回调返回值转换为目标类型

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 01:20:23