如何将包含两个参数包的requires子句提取为C++ concept?
问题分析与解决方案
核心错误原因
- 你最初写的内联
requires约束是合法的:(std::same_as<std::remove_cvref_t<Ts>, std::remove_cvref_t<CTs>> && ...)这个折叠表达式会逐位置对应匹配两个参数包:Ts的第N个元素和CTs的第N个元素对比,要求两个包长度天然一致,且对应位置去cvref限定符后类型完全相同。 - 独立
concept定义出错的核心问题是:C++的模板参数列表中,只有最后一个参数包支持自动推导,前面的参数包是贪婪匹配的。你写的template <typename... T1s, typename... T2s>中,当你传入same_unqualified_types<Ts..., CTs...>时,所有的模板实参都会被匹配给T1s,T2s永远是空包,自然会出现「参数包长度不匹配」的折叠错误。
正确的concept实现方案
你只需要把两个参数包分别包装为独立的类型(比如用std::tuple承载),就可以避开多参数包的推导问题,完全等价的实现如下:
#include <type_traits> #include <iostream> #include <tuple> // 辅助实现:特化接收两个tuple包裹的参数包 template <typename Pack1, typename Pack2> struct same_unqualified_types_impl; template <typename... T1s, typename... T2s> struct same_unqualified_types_impl<std::tuple<T1s...>, std::tuple<T2s...>> : std::bool_constant<(std::same_as<std::remove_cvref_t<T1s>, std::remove_cvref_t<T2s>> && ...)> { // 可选:显式校验包长度一致,报错信息更清晰 static_assert(sizeof...(T1s) == sizeof...(T2s), "Two parameter packs must have the same length"); }; // 最终对外的concept template <typename Pack1, typename Pack2> concept same_unqualified_types = same_unqualified_types_impl<Pack1, Pack2>::value; template <typename... Ts> struct foo { template <typename... CTs> // 把两个参数包分别用tuple包装后传入concept requires same_unqualified_types<std::tuple<Ts...>, std::tuple<CTs...>> foo(CTs&& ...) { std::cout << "called with " << (sizeof...(CTs)) << " args\n"; } }; int main() { auto f1 = foo<int, int>(1, 1); // 编译通过 // auto f2 = foo<float, int>(1, 1); // 编译报错,符合预期 return 0; }
简化写法(可选)
如果不想引入额外的辅助结构体,也可以直接在concept里通过lambda + requires表达式完成校验,逻辑完全一致:
template <typename Pack1, typename Pack2> concept same_unqualified_types = requires { requires []<typename... T1s, typename... T2s>(std::tuple<T1s...>, std::tuple<T2s...>) { return (std::same_as<std::remove_cvref_t<T1s>, std::remove_cvref_t<T2s>> && ...); }(std::declval<Pack1>(), std::declval<Pack2>()); };
内容的提问来源于stack exchange,提问作者Fureeish
相关产品推荐
相关产品推荐

