C++20中能否将概念作为模板参数定义通用约束概念?
在C++20中传递概念作为模板参数的方法
我已经在C++20中实现了两个约束范围元素的概念:
约束元素为浮点类型的RangeOfFloats
template <typename R> concept RangeOfFloats = std::ranges::range<R> && std::floating_point<std::ranges::range_value_t<R>>; static_assert(RangeOfFloats<std::span<float>>); static_assert(RangeOfFloats<std::span<double>>);
约束元素为精确类型的RangeOfType
template <typename R, typename T> concept RangeOfType = std::ranges::range<R> && std::is_same_v<std::ranges::range_value_t<R>, T>; static_assert(RangeOfType<std::span<float>, float>); static_assert(!RangeOfType<std::span<float>, double>);
现在我希望定义一个通用概念RangeOfThingsLike,能够接受另一个概念作为模板参数来约束范围元素,期望的语法如下:
template <typename R, template <typename> concept ElementConstraint> concept RangeOfThingsLike = std::ranges::range<R> && ElementConstraint<std::ranges::range_value_t<R>>; static_assert(RangeOfThingsLike<std::span<float>, std::floating_point>);
但上述语法是无效的。我尝试用类型特性替代,实现如下:
template <typename R, template <typename> typename ElementConstraint> concept RangeOfThingsLike = std::ranges::range<R> && ElementConstraint<std::ranges::range_value_t<R>>::value; static_assert(RangeOfThingsLike<std::span<float>, std::is_floating_point>);
但这种方式不够优雅。请问在C++20中是否存在将概念作为模板参数传递的有效方法?
解答
C++20不支持直接将概念作为模板模板参数(即template <typename> concept ElementConstraint这种写法不符合标准语法),但可以通过以下几种更优雅的替代方案实现类似需求:
方案1:将概念包装为constexpr模板变量
把目标概念包装成一个constexpr模板布尔变量,作为谓词传递给通用概念:
#include <ranges> #include <concepts> #include <span> // 包装std::floating_point概念为constexpr模板变量 template <typename T> constexpr bool is_floating_point = std::floating_point<T>; // 通用范围概念,接受谓词模板 template <typename R, template <typename> typename Pred> concept RangeOfThingsLike = std::ranges::range<R> && Pred<std::ranges::range_value_t<R>>; // 使用示例 static_assert(RangeOfThingsLike<std::span<float>, is_floating_point>); static_assert(RangeOfThingsLike<std::span<double>, is_floating_point>);
方案2:使用C++20的constexpr lambda作为非类型模板参数
利用C++20允许constexpr lambda作为非类型模板参数的特性,直接传递概念检查逻辑:
#include <ranges> #include <concepts> #include <span> // 通用范围概念,接受一个可调用的constexpr谓词 template <typename R, auto Pred> concept RangeOfThingsLike = std::ranges::range<R> && Pred.template operator()<std::ranges::range_value_t<R>>(); // 使用示例:传递检查std::floating_point的lambda static_assert(RangeOfThingsLike<std::span<float>, []<typename T>() constexpr { return std::floating_point<T>; }>);
方案3:直接在使用时结合requires子句(简化场景)
如果不需要定义通用的RangeOfThingsLike概念,也可以直接在模板或函数中用requires子句组合范围约束和元素概念:
#include <ranges> #include <concepts> #include <span> template <std::ranges::range R> requires std::floating_point<std::ranges::range_value_t<R>> void process_float_range(R&& range) { // 处理浮点类型范围 } // 使用示例 void test() { std::span<float> floats{}; process_float_range(floats); // 合法 }
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

