如何优化模板类中不同容器元素的自定义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
相关产品推荐
相关产品推荐

