如何避免不同参数类型的std::function发生隐式转换?
解决std::function隐式转换导致的构造函数歧义问题
在绑定TA-Lib函数并实现回调的场景中,创建X对象y时出现构造函数调用歧义:std::function<int(int)>或int(*)(int)可以隐式转换为std::function<int(double)>,导致匹配int和double版本的构造函数时产生歧义,添加explicit关键字也无法解决。以下是几种可行的解决方案:
方法1:标签分派(Tag Dispatch)
通过添加类型区分标签,让编译器明确匹配目标构造函数,消除歧义。
修改后的代码示例
#include <functional> #include <type_traits> #include <cstdint> struct DataChunk { // ... }; typedef uint64_t idx_t; template <typename LookbackArgType> // int, double struct talib_traits { using talib_lookback_t = std::function<int(LookbackArgType)>; using talib_function_t = std::function<void(DataChunk &, void *, idx_t, idx_t, LookbackArgType)>; }; template <> struct talib_traits<void> { using talib_lookback_t = std::function<int()>; using talib_function_t = std::function<void(DataChunk &, void *, idx_t, idx_t)>; }; // 定义类型区分标签 struct int_tag {}; struct double_tag {}; struct void_tag {}; struct X { talib_traits<int>::talib_lookback_t talib_lookback_int = nullptr; talib_traits<int>::talib_function_t talib_function_int = nullptr; talib_traits<double>::talib_lookback_t talib_lookback_double = nullptr; talib_traits<double>::talib_function_t talib_function_double = nullptr; talib_traits<void>::talib_lookback_t talib_lookback_void = nullptr; talib_traits<void>::talib_function_t talib_function_void = nullptr; explicit X(int_tag, talib_traits<int>::talib_lookback_t talib_lookback, talib_traits<int>::talib_function_t talib_function) : talib_lookback_int(talib_lookback), talib_function_int(talib_function) {} explicit X(double_tag, talib_traits<double>::talib_lookback_t talib_lookback, talib_traits<double>::talib_function_t talib_function) : talib_lookback_double(talib_lookback), talib_function_double(talib_function) {} explicit X(void_tag, talib_traits<void>::talib_lookback_t talib_lookback, talib_traits<void>::talib_function_t talib_function) : talib_lookback_void(talib_lookback), talib_function_void(talib_function) {} }; int main() { constexpr bool lookback_is_same = std::is_same<talib_traits<int>::talib_lookback_t, talib_traits<double>::talib_lookback_t>::value; constexpr bool function_is_same = std::is_same<talib_traits<int>::talib_function_t, talib_traits<double>::talib_function_t>::value; static_assert(!lookback_is_same && !function_is_same); X x(void_tag{}, []() { return 0; }, [](DataChunk &, void *, idx_t, idx_t) {}); // 正常 // 通过标签明确指定匹配int版本构造函数 X y(int_tag{}, [](int) { return 0; }, [](DataChunk &, void *, idx_t, idx_t, int) {}); }
方法2:SFINAE约束构造函数精确匹配
利用SFINAE特性,让构造函数仅接受参数类型完全匹配的可调用对象,避免隐式转换导致的歧义。
修改后的代码示例
#include <functional> #include <type_traits> #include <cstdint> struct DataChunk { // ... }; typedef uint64_t idx_t; template <typename LookbackArgType> // int, double struct talib_traits { using talib_lookback_t = std::function<int(LookbackArgType)>; using talib_function_t = std::function<void(DataChunk &, void *, idx_t, idx_t, LookbackArgType)>; }; template <> struct talib_traits<void> { using talib_lookback_t = std::function<int()>; using talib_function_t = std::function<void(DataChunk &, void *, idx_t, idx_t)>; }; struct X { talib_traits<int>::talib_lookback_t talib_lookback_int = nullptr; talib_traits<int>::talib_function_t talib_function_int = nullptr; talib_traits<double>::talib_lookback_t talib_lookback_double = nullptr; talib_traits<double>::talib_function_t talib_function_double = nullptr; talib_traits<void>::talib_lookback_t talib_lookback_void = nullptr; talib_traits<void>::talib_function_t talib_function_void = nullptr; // 约束仅匹配int类型参数的可调用对象 template<typename LookbackFunc, typename Func, typename = std::enable_if_t<std::is_invocable_r_v<int, LookbackFunc, int> && std::is_invocable_r_v<void, Func, DataChunk&, void*, idx_t, idx_t, int>>> explicit X(LookbackFunc&& lookback_func, Func&& func) : talib_lookback_int(std::forward<LookbackFunc>(lookback_func)), talib_function_int(std::forward<Func>(func)) {} // 约束仅匹配double类型参数的可调用对象 template<typename LookbackFunc, typename Func, typename = std::enable_if_t<std::is_invocable_r_v<int, LookbackFunc, double> && std::is_invocable_r_v<void, Func, DataChunk&, void*, idx_t, idx_t, double>>> explicit X(LookbackFunc&& lookback_func, Func&& func) : talib_lookback_double(std::forward<LookbackFunc>(lookback_func)), talib_function_double(std::forward<Func>(func)) {} // 约束仅匹配无额外参数的可调用对象 template<typename LookbackFunc, typename Func, typename = std::enable_if_t<std::is_invocable_r_v<int, LookbackFunc> && std::is_invocable_r_v<void, Func, DataChunk&, void*, idx_t, idx_t>>> explicit X(LookbackFunc&& lookback_func, Func&& func) : talib_lookback_void(std::forward<LookbackFunc>(lookback_func)), talib_function_void(std::forward<Func>(func)) {} }; int main() { constexpr bool lookback_is_same = std::is_same<talib_traits<int>::talib_lookback_t, talib_traits<double>::talib_lookback_t>::value; constexpr bool function_is_same = std::is_same<talib_traits<int>::talib_function_t, talib_traits<double>::talib_function_t>::value; static_assert(!lookback_is_same && !function_is_same); X x([]() { return 0; }, [](DataChunk &, void *, idx_t, idx_t) {}); // 正常 // 编译器会自动匹配int版本构造函数,无歧义 X y([](int) { return 0; }, [](DataChunk &, void *, idx_t, idx_t, int) {}); }
方法3:模板构造函数+显式类型推导
将构造函数改为模板形式,通过显式指定模板参数明确匹配的类型,消除歧义。
修改后的代码示例
#include <functional> #include <type_traits> #include <cstdint> struct DataChunk { // ... }; typedef uint64_t idx_t; template <typename LookbackArgType> // int, double struct talib_traits { using talib_lookback_t = std::function<int(LookbackArgType)>; using talib_function_t = std::function<void(DataChunk &, void *, idx_t, idx_t, LookbackArgType)>; }; template <> struct talib_traits<void> { using talib_lookback_t = std::function<int()>; using talib_function_t = std::function<void(DataChunk &, void *, idx_t, idx_t)>; }; struct X { talib_traits<int>::talib_lookback_t talib_lookback_int = nullptr; talib_traits<int>::talib_function_t talib_function_int = nullptr; talib_traits<double>::talib_lookback_t talib_lookback_double = nullptr; talib_traits<double>::talib_function_t talib_function_double = nullptr; talib_traits<void>::talib_lookback_t talib_lookback_void = nullptr; talib_traits<void>::talib_function_t talib_function_void = nullptr; template<typename LookbackArgType> explicit X(talib_traits<LookbackArgType>::talib_lookback_t talib_lookback, talib_traits<LookbackArgType>::talib_function_t talib_function) { if constexpr (std::is_same_v<LookbackArgType, int>) { talib_lookback_int = talib_lookback; talib_function_int = talib_function; } else if constexpr (std::is_same_v<LookbackArgType, double>) { talib_lookback_double = talib_lookback; talib_function_double = talib_function; } else if constexpr (std::is_same_v<LookbackArgType, void>) { talib_lookback_void = talib_lookback; talib_function_void = talib_function; } } }; int main() { constexpr bool lookback_is_same = std::is_same<talib_traits<int>::talib_lookback_t, talib_traits<double>::talib_lookback_t>::value; constexpr bool function_is_same = std::is_same<talib_traits<int>::talib_function_t, talib_traits<double>::talib_function_t>::value; static_assert(!lookback_is_same && !function_is_same); X x<void>([]() { return 0; }, [](DataChunk &, void *, idx_t, idx_t) {}); // 正常 // 显式指定模板参数为int,明确匹配对应版本 X y<int>([](int) { return 0; }, [](DataChunk &, void *, idx_t, idx_t, int) {}); }
内容的提问来源于stack exchange,提问作者Mark Taylor
相关产品推荐
相关产品推荐

