如何在C++模板函数中解引用任意容器迭代器至指定类型?
我希望实现一个可接收任意类型容器、对其元素进行操作的函数。目前基于迭代器的模板函数仅支持可直接解引用到SomeObject的容器(如std::vector<SomeObject>),想扩展支持std::vector<std::shared_ptr<SomeObject>>、std::map<int, SomeObject>这类容器,于是尝试让用户传入std::function类型的解引用函数,但编译报错。
代码实现
#include <functional> #include <iostream> #include <string> #include <map> #include <memory> class SomeObject { public: SomeObject(int id, const std::string& name) : id(id), name(name) { } int getId() const { return id; } void doSomething() const { std::cout << name << std::endl; } private: int id; std::string name; }; template <typename Iter> void doSomethingWithMultipleObjectsInAnArbitraryContainer(Iter begin, Iter end, std::function<const SomeObject& (Iter)> dereferenceIterToObject) { if (begin == end) { // 容器为空,无需执行任何操作 return; } for (Iter it = begin; it != end; ++it) { const SomeObject& obj = dereferenceIterToObject(it); // 对对象执行操作 obj.doSomething(); } } int main(int argc, char** argv) { SomeObject obj1{1, "one"}; SomeObject obj2{2, "two"}; SomeObject obj3{3, "three"}; std::map<int, SomeObject> mapWithValues { {obj1.getId(), obj1}, {obj2.getId(), obj2}, {obj3.getId(), obj3} }; std::map<int, std::shared_ptr<SomeObject>> mapWithPointers { {obj1.getId(), std::make_shared<SomeObject>(obj1)}, {obj2.getId(), std::make_shared<SomeObject>(obj2)}, {obj3.getId(), std::make_shared<SomeObject>(obj3)} }; std::vector<SomeObject> vectorWithValues { obj1, obj2, obj3 }; doSomethingWithMultipleObjectsInAnArbitraryContainer(mapWithValues.cbegin(), mapWithValues.cend(), [](std::map<int, SomeObject>::const_iterator it) -> const SomeObject& { return it->second; }); // 此处编译报错 doSomethingWithMultipleObjectsInAnArbitraryContainer(mapWithPointers.cbegin(), mapWithPointers.cend(), [](std::map<int, std::shared_ptr<SomeObject>>::const_iterator it) -> const SomeObject& { return *it->second; }); // 此处编译报错 doSomethingWithMultipleObjectsInAnArbitraryContainer(vectorWithValues.cbegin(), vectorWithValues.cend(), [](std::vector<SomeObject>::const_iterator it) -> const SomeObject& { return *it; }); // 此处编译报错 return 0; }
编译错误信息
错误:找不到匹配的函数调用‘doSomethingWithMultipleObjectsInAnArbitraryContainer(std::map<int, SomeObject>::const_iterator, std::map<int, SomeObject>::const_iterator, main(int, char**)::<lambda(std::map<int, SomeObject>::const_iterator)>)’
});
注:候选函数为:‘template void doSomethingWithMultipleObjectsInAnArbitraryContainer(Iter, Iter, std::function<const SomeObject&(Iter)>)’
void doSomethingWithMultipleObjectsInAnArbitraryContainer(Iter begin, Iter end, std::function<const SomeObject& (Iter)> dereferenceIterToObject) {
注:模板参数推导/替换失败:
test_module/IecTestServer.cpp:131:4: 注:‘main(int, char**)::<lambda(std::map<int, SomeObject>::const_iterator)>’并非派生自‘std::function<const SomeObject&(Iter)>’
});
编译错误原因
模板参数推导阶段,Iter的类型需要从前两个参数推导,但第三个参数std::function<const SomeObject& (Iter)>也依赖Iter的类型。而lambda表达式无法隐式转换为std::function(因为Iter还未确定),导致模板推导失败。
修复方案1:显式指定模板参数
调用函数时显式声明Iter的类型,让编译器提前确定模板参数,这样lambda可以正常转换为std::function:
// 调用mapWithValues时显式指定迭代器类型 doSomethingWithMultipleObjectsInAnArbitraryContainer<std::map<int, SomeObject>::const_iterator>( mapWithValues.cbegin(), mapWithValues.cend(), [](std::map<int, SomeObject>::const_iterator it) -> const SomeObject& { return it->second; }); // 调用mapWithPointers时同理 doSomethingWithMultipleObjectsInAnArbitraryContainer<std::map<int, std::shared_ptr<SomeObject>>::const_iterator>( mapWithPointers.cbegin(), mapWithPointers.cend(), [](std::map<int, std::shared_ptr<SomeObject>>::const_iterator it) -> const SomeObject& { return *it->second; }); // 调用vectorWithValues时同理 doSomethingWithMultipleObjectsInAnArbitraryContainer<std::vector<SomeObject>::const_iterator>( vectorWithValues.cbegin(), vectorWithValues.cend(), [](std::vector<SomeObject>::const_iterator it) -> const SomeObject& { return *it; });
修复方案2:将解引用函数改为模板参数(更优)
放弃std::function,直接把解引用逻辑作为模板类型参数,这样编译器可以直接推导lambda的类型,避免转换开销,代码更简洁:
// 修改后的模板函数 template <typename Iter, typename DereferenceFunc> void doSomethingWithMultipleObjectsInAnArbitraryContainer(Iter begin, Iter end, DereferenceFunc deref) { if (begin == end) { return; } for (Iter it = begin; it != end; ++it) { const SomeObject& obj = deref(it); obj.doSomething(); } } // 调用时无需显式指定模板参数,直接传lambda即可 doSomethingWithMultipleObjectsInAnArbitraryContainer(mapWithValues.cbegin(), mapWithValues.cend(), [](auto it) -> const SomeObject& { return it->second; }); doSomethingWithMultipleObjectsInAnArbitraryContainer(mapWithPointers.cbegin(), mapWithPointers.cend(), [](auto it) -> const SomeObject& { return *it->second; }); doSomethingWithMultipleObjectsInAnArbitraryContainer(vectorWithValues.cbegin(), vectorWithValues.cend(), [](auto it) -> const SomeObject& { return *it; });
进阶优化:添加默认解引用逻辑
对于可以直接解引用到SomeObject的迭代器,提供默认的解引用函数,这样调用时无需手动传入lambda:
// 默认解引用函数:直接解引用迭代器 template <typename Iter> const SomeObject& defaultDeref(Iter it) { return *it; } // 修改模板函数,添加默认参数 template <typename Iter, typename DereferenceFunc = decltype(defaultDeref<Iter>)> void doSomethingWithMultipleObjectsInAnArbitraryContainer(Iter begin, Iter end, DereferenceFunc deref = defaultDeref<Iter>) { if (begin == end) { return; } for (Iter it = begin; it != end; ++it) { const SomeObject& obj = deref(it); obj.doSomething(); } } // 调用vectorWithValues时无需传第三个参数 doSomethingWithMultipleObjectsInAnArbitraryContainer(vectorWithValues.cbegin(), vectorWithValues.cend()); // map类型仍需传入自定义lambda doSomethingWithMultipleObjectsInAnArbitraryContainer(mapWithValues.cbegin(), mapWithValues.cend(), [](auto it) -> const SomeObject& { return it->second; });
内容的提问来源于stack exchange,提问作者Sylvester

