可调用对象最大参数数量推导工具在C++概念(Concepts)场景下失效的问题求助
可调用对象最大参数数量推导工具在C++概念(Concepts)场景下失效的问题求助
大家好,我最近实现了一个probeMaxArgs模板函数,用来推导可调用对象(假设是没有重载operator()的简单lambda)能接受的最大参数数量。我的实现思路是借助一个可以隐式转换为任意类型的probe类型,通过模板尝试传入不同数量的probe参数,检测可调用对象是否能被正常调用。
具体的实现代码如下:
namespace detail { namespace { struct probe { template<typename T> consteval operator T const &() const noexcept; template<typename T> consteval operator T const &&() const noexcept; template<typename T> consteval operator T&() const noexcept; template<typename T> consteval operator T&&() const noexcept; }; template<typename T, size_t N = 10> requires(N < 15) consteval size_t probeMaxArgs() { constexpr auto probeN = []<size_t... I>(std::index_sequence<I...>) -> bool { return std::is_invocable_v<T&, decltype((void)I, probe{})...> || std::is_invocable_v<T&&, decltype((void)I, probe{})...>; }; constexpr std::array results = [&]<size_t... I>(std::index_sequence<I...>) { return std::array{probeN(std::make_index_sequence<I>())...}; }(std::make_index_sequence<N>()); constexpr size_t result = [&] { size_t r = size_t(-1); for (size_t i = 0; i < results.size(); ++i) r = results[i] ? i : r; return r; }(); static_assert(result != size_t(-1), "Failed to deduce max args"); return result; } } // namespace } // namespace detail
目前这个工具在大部分场景下都能正常工作,比如普通无参lambda、单参数模板lambda、多参数模板lambda,甚至带默认参数的lambda,下面这些静态断言都能顺利通过:
static_assert(0 == detail::probeMaxArgs<decltype([]{})>()); static_assert(1 == detail::probeMaxArgs<decltype([](auto){})>()); static_assert(3 == detail::probeMaxArgs<decltype([](auto, auto, auto){})>()); static_assert(3 == detail::probeMaxArgs<decltype([](auto, auto, auto = 4){})>());
但遇到带C++概念(Concepts)约束的场景时,整个工具就失效了,编译直接失败。比如下面这个lambda:
constexpr auto sort = []< // std::ranges::random_access_range R, //< 取消注释就编译失败 typename R, //< 用这个就能正常编译 typename Comp = std::ranges::less, typename Proj = std::identity >(R&& r, Comp comp = {}, Proj proj = {}) // -> std::ranges::borrowed_range auto { //< 取消注释就编译失败 { // std::ranges::sort(r, comp, proj); //< 取消注释后,即使调用probeMaxArgs也会让这行编译失败 return std::forward<R>(r); }; static_assert(3 == detail::probeMaxArgs<decltype(sort)>());
只要取消上面任意一行带概念的注释,编译就会失败;甚至如果取消std::ranges::sort的调用注释,同时调用probeMaxArgs,那std::ranges::sort这行也会报错。
我还尝试过另一种思路,通过提取lambda的operator()模板来推导参数数量,但这个方法有明显的局限性:它无法处理非类型模板参数,而且如果要覆盖所有可能的参数组合,编译时间会急剧增加,不太实用:
template <typename F> struct get { template <typename T> static constexpr auto op = &F::template operator()<T>; }; using get_l = get<decltype([]<std::ranges::random_access_range R>(R&& r){})>; // 能正常编译 template <typename T> constexpr auto op_l = get_l::op<T>; // 编译失败:lambda只有一个参数 // template <typename T, typename T_1> // constexpr auto op_l = get_l::op<T, T_1>;
想请教一下各位大佬,为什么带概念约束的场景会导致我的probeMaxArgs失效?有没有什么办法可以解决这个问题,让工具能兼容带概念的可调用对象?
内容来源于stack exchange
相关产品推荐
相关产品推荐

