C++20以函数类型为参数的Concept:如何简洁实现并遵循DRY原则
优化泛型二元操作的Concept实现
核心思路是把操作逻辑和类型约束解耦:让Concept只负责判断类型是否支持"分发操作到成员"的能力,具体操作逻辑只在函数内部实现一次,不再重复写到Concept约束里。
步骤1:定义只关注类型能力的Concept
不再让Concept依赖具体的操作函数,而是检查类型是否提供统一的成员访问接口(比如apply方法),或者通过SFINAE判断能否对其成员应用二元操作:
#include <concepts> #include <utility> #include <tuple> // 定义Distributive Concept:判断类型T是否支持二元操作分发 template<typename T> concept Distributive = requires(T a, T b, double (*op)(double, double)) { { a.apply(op, b) } -> std::same_as<T>; }; // 基础类型double直接适配Concept template<> concept Distributive<double> = true;
步骤2:实现统一的分发工具函数
把原来重复的Lambda逻辑封装到distribute函数中,操作逻辑只写一次:
// 基础类型的分发:直接调用操作符 double distribute(double a, double b, auto op) { return op(a, b); } // 针对Distributive类型的分发:调用类型自身的apply方法 template<Distributive T> T distribute(T a, T b, auto op) { return a.apply(op, b); } // 示例聚合类型foo:实现apply方法处理成员 template<typename... Args> struct foo { std::tuple<Args...> members; template<typename Op> foo apply(Op op, const foo& other) const { return {std::apply([&](auto&&... args) { return std::make_tuple(op(args, std::get<decltype(args)>(other.members))...); }, members)}; } }; // 嵌套类型bar:递归调用apply处理内部成员 struct bar { foo<double, double> inner; template<typename Op> bar apply(Op op, const bar& other) const { return {inner.apply(op, other.inner)}; } };
步骤3:简洁的binop实现
现在binop的约束只需要用Distributive,不需要重复Lambda逻辑,直接用Distributive auto即可:
// 泛型二元操作,约束简洁明了 auto binop(Distributive auto a, Distributive auto b, auto op) { return distribute(a, b, op); } // 使用示例 int main() { double d1 = 1.0, d2 = 2.0; auto d_res = binop(d1, d2, [](double x, double y) { return x + y; }); foo<double> f1{{3.0}}, f2{{4.0}}; auto f_res = binop(f1, f2, [](double x, double y) { return x * y; }); bar b1{{{5.0, 6.0}}}, b2{{{7.0, 8.0}}}; auto b_res = binop(b1, b2, [](double x, double y) { return x - y; }); }
关键优化点
- 解耦类型约束与操作逻辑:Concept只关注类型是否具备分发能力,不绑定具体操作,彻底避免Lambda重复。
- 统一分发入口:通过
distribute函数集中处理不同类型的操作分发,逻辑只维护一次。 - 语法简洁性:直接使用
Distributive auto,完全符合你期望的简洁写法。
内容的提问来源于stack exchange,提问作者Involute
相关产品推荐
相关产品推荐

