VS下C++17模板推导失败:重载成员函数指针匹配问题
解决Visual Studio中std::map重载成员函数的模板推导问题
以下代码可在GCC与Clang编译器下正常编译,目标是保存不同map实现(如std::map、QMap等)的成员函数指针:
#include <type_traits> #include <string> #include <map> struct MapRegistration { template <template <typename, typename, typename...> class MapT, typename KeyType, typename ValueType, typename... Rest, typename Map = MapT<KeyType, ValueType, Rest...>> static void from(ValueType& (MapT<KeyType, ValueType, Rest...>::*insertMethod)(KeyType const&)) { } }; int main() { MapRegistration::from(&std::map<std::string, double>::operator[]); return 0; }
但在Visual Studio中,由于std::map的operator[]存在重载,编译时会出现如下错误:
<source>(15): error C2672: 'MapRegistration::from': no matching overloaded function found <source>(8): note: could be 'void MapRegistration::from(ValueType &(__cdecl MapT<KeyType,ValueType,Rest...>::* )(const KeyType &))' <source>(15): note: 'void MapRegistration::from(ValueType &(__cdecl MapT<KeyType,ValueType,Rest...>::* )(const KeyType &))': could not deduce template argument for 'MapT' <source>(15): note: 'void MapRegistration::from(ValueType &(__cdecl MapT<KeyType,ValueType,Rest...>::* )(const KeyType &))': could not deduce template argument for 'KeyType' <source>(15): note: 'void MapRegistration::from(ValueType &(__cdecl MapT<KeyType,ValueType,Rest...>::* )(const KeyType &))': could not deduce template argument for 'ValueType' Compiler returned: 2
生产代码场景补充
实际开发的元类型系统需要高度通用性,支持任意map类型与键值组合,以下是生产代码的核心片段:
struct MapRegistration { template <typename Map, typename KeyType, typename ValueType> MapRegistration(std::string mapName, ValueType& (Map::*insertMethod)(KeyType const&)) : mapName(mapName) { using InsertMethod = ValueType& (Map::*)(KeyType const&); insertFunction = [mapName, insertMethod](std::any& mapAny, std::any const& keyAny, std::any&& valueAny) { Map* collection = std::any_cast<Map>(&mapAny); // 错误处理逻辑省略 ValueType* value = std::any_cast<ValueType>(&valueAny); KeyType const* key = std::any_cast<KeyType>(&keyAny); constexpr bool hasCorrectInsertMethod = std::is_invocable_r<ValueType&, InsertMethod, Map, KeyType const&>(); static_assert(hasCorrectInsertMethod, "Insert Method is not correct!"); std::invoke(insertMethod, *collection, *key) = *value; }; } std::string mapName; std::function<void(std::any&, std::any const&, std::any&&)> insertFunction; };
解决方案
针对Visual Studio的模板推导失败问题,可通过以下几种方式解决:
1. 显式类型转换指定重载版本
在调用时,将成员函数指针强制转换为目标类型,明确告诉编译器要选择的重载:
int main() { using TargetMap = std::map<std::string, double>; // 显式指定operator[]的重载类型 auto op = static_cast<double& (TargetMap::*)(const std::string&)>(&TargetMap::operator[]); MapRegistration::from(op); // 对应生产代码的构造调用同理: MapRegistration reg("std::map", op); return 0; }
2. 优化模板参数设计,简化推导逻辑
将原模板中的模板模板参数改为直接接受Map类型,从Map内部提取键值类型,这样编译器更容易推导:
struct MapRegistration { // 直接接受Map类型,从Map中推导Key和Value template <typename Map> static void from(typename Map::mapped_type& (Map::*insertMethod)(const typename Map::key_type&)) { } }; // 调用时无需额外转换,VS可正常推导 int main() { MapRegistration::from(&std::map<std::string, double>::operator[]); return 0; }
3. 为生产代码添加辅助推导重载
针对生产代码的构造函数,可添加一个辅助模板,利用Map的内部类型自动推导键值类型,避免手动指定:
struct MapRegistration { // 原构造函数保留,用于手动指定类型的场景 template <typename Map, typename KeyType, typename ValueType> MapRegistration(std::string mapName, ValueType& (Map::*insertMethod)(KeyType const&)) : mapName(std::move(mapName)) { // 原逻辑不变 } // 辅助构造函数,自动从Map提取键值类型 template <typename Map> MapRegistration(std::string mapName, typename Map::mapped_type& (Map::*insertMethod)(const typename Map::key_type&)) : MapRegistration(std::move(mapName), static_cast<typename Map::mapped_type& (Map::*)(const typename Map::key_type&)>(insertMethod)) { } std::string mapName; std::function<void(std::any&, std::any const&, std::any&&)> insertFunction; }; // 调用时直接传成员函数指针即可 int main() { MapRegistration reg("std::map", &std::map<std::string, double>::operator[]); return 0; }
这些方法既保证了通用性,又解决了Visual Studio下的模板推导问题,同时兼容GCC和Clang编译器。
内容的提问来源于stack exchange,提问作者Stefano
相关产品推荐
相关产品推荐

