英文:
CRTP and recursively defining a concept
问题
有关此问题已经有了一些问题(例如 https://stackoverflow.com/questions/40853060/c-concepts-crtp ),但据我所知,答案并没有真正解决根本问题的方法。
我想要递归地定义一个概念。大致如下。如果一个类型是整数,那么它就是 concept_0
的模型。如果一个类型是 concept_n
元素的范围,那么它就是 concept_{n+1}
的模型。我希望以下类似的代码能够编译通过:
template <typename R>
concept my_object = std::integral<R> || (std::ranges::sized_range<R> && my_object<typename R::value_type>);
有办法实现这个吗?
英文:
There are questions already regarding this (eg. https://stackoverflow.com/questions/40853060/c-concepts-crtp) but afaict the answers do not really say how to solve the underlying issue.
I want to define recursively a concept. Something along the following lines. A type models concept_0
if it is integral. And a type models concept_{n+1}
if it is a range of concept_n
elements. I would like something like the following to compile
template <typename R>
concept my_object = std::integral<R> || (std::ranges::sized_range<R> && my_object<typename R::value_type>);
Is there a way to achieve this?
答案1
得分: 4
AFAIK, concepts do not allow recursion. But templates do, so to get your example to work, you could write a template class that models your concept and then just define your concept as a thin wrapper around that:
template <typename R>
struct my_object : std::false_type
{};
// specialization for integral types
template <std::integral R>
struct my_object<R>
: public std::true_type
{};
// specialization for ranges using recursion
template <std::ranges::sized_range R>
struct my_object<R>
: public std::conditional_t<
my_object<typename R::value_type>::value,
std::true_type,
std::false_type
>
{};
// wrapping in a concept
template <typename R>
concept my_object_concept = my_object<R>::value;
英文:
AFAIK, concepts do not allow recursion. But templates do, so to get your example to work, you could write a template class that models your concept and then just define your concept as a thin wrapper around that:
template <typename R>
struct my_object : std::false_type
{};
// specialization for integral types
template <std::integral R>
struct my_object<R>
: public std::true_type
{};
// specialization for ranges using recursion
template <std::ranges::sized_range R>
struct my_object<R>
: public std::conditional_t<
my_object<typename R::value_type>::value,
std::true_type,
std::false_type
>
{};
// wrapping in a concept
template <typename R>
concept my_object_concept = my_object<R>::value;
答案2
得分: 3
请看老式的方法:
#include <concepts>
#include <ranges>
#include <vector>
template <typename R>
struct is_my_object
{
static constexpr bool value = false;
};
template <std::integral R>
struct is_my_object<R>
{
static constexpr bool value = true;
};
template <std::ranges::sized_range R>
struct is_my_object<R>
{
static constexpr bool value = is_my_object<typename R::value_type>::value;
};
template <typename R>
concept my_object = is_my_object<R>::value;
static_assert(my_object<int>);
static_assert(!my_object<float>);
static_assert(my_object<std::vector<int>>);
static_assert(my_object<std::vector<std::vector<int>>>);
(This definition will fail on `my_object<int[2]>`; fixing this problem is an easy exercise).
英文:
Do it the old-fashioned way:
#include <concepts>
#include <ranges>
#include <vector>
template <typename R>
struct is_my_object
{
static constexpr bool value = false;
};
template <std::integral R>
struct is_my_object<R>
{
static constexpr bool value = true;
};
template <std::ranges::sized_range R>
struct is_my_object<R>
{
static constexpr bool value = is_my_object<typename R::value_type>::value;
};
template <typename R>
concept my_object = is_my_object<R>::value;
static_assert(my_object<int>);
static_assert(!my_object<float>);
static_assert(my_object<std::vector<int>>);
static_assert(my_object<std::vector<std::vector<int>>>);
(This definition will fail on my_object<int[2]>
; fixing this problem is an easy exercise).
答案3
得分: 1
你的类型特征实际上不需要三个模板定义来实现这个概念,因为只有两种情况要处理。此外,使用 std::ranges::range_value_t<R>
而不是 typename R::value_type
允许你检查 C 风格数组类型。
#include <concepts>
#include <ranges>
#include <type_traits>
#include <vector>
template <class R>
struct is_my_object : std::is_integral<R> {};
template <std::ranges::sized_range R>
struct is_my_object<R> : is_my_object<std::ranges::range_value_t<R>> {};
template <class R>
concept my_object = is_my_object<R>::value;
static_assert(my_object<int>);
static_assert(not my_object<float>);
static_assert(my_object<std::vector<int>>);
static_assert(my_object<std::vector<std::vector<int>>>);
static_assert(my_object<int[2]>);
static_assert(not my_object<int[]>);
https://godbolt.org/z/5Krb6cosv
英文:
Your type trait doesn't actually need three template definitions in order to implement this concept, since there's only two cases to handle. Also, using std::ranges::range_value_t<R>
instead of typename R::value_type
allows you to check C-style array types as well.
#include <concepts>
#include <ranges>
#include <type_traits>
#include <vector>
template <class R>
struct is_my_object : std::is_integral<R> {};
template <std::ranges::sized_range R>
struct is_my_object<R> : is_my_object<std::ranges::range_value_t<R>> {};
template <class R>
concept my_object = is_my_object<R>::value;
static_assert(my_object<int>);
static_assert(not my_object<float>);
static_assert(my_object<std::vector<int>>);
static_assert(my_object<std::vector<std::vector<int>>>);
static_assert(my_object<int[2]>);
static_assert(not my_object<int[]>);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论