最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - How to check template parameter is a container of fixed length N using concepts? - Stack Overflow

programmeradmin2浏览0评论

I would like to have a concept that checks if a type is a container of fixed length. The type could be a C-style array, a std::array, a std::span or a custom-made container. I can check for the container condition as shown in the code below, but I cannot get it to check for the length condition.

template <typename T, size_t N>
concept fixed_container =
   requires (T const& t)
   {
       {std::size(t)} -> std::same_as<size_t>;
       {std::begin(t)};
       {std::end(t)};
   }
   // && std::declval<T>().size() == N // Error: 'declval() must not be used!'
   // && T::size() == N // Error: 'call to non-static member function without an object argument'
   ;

// These should all be correct
static_assert( fixed_container<std::array<double, 3>, 3>);
static_assert(!fixed_container<std::array<double, 3>, 4>);
static_assert( fixed_container<std::span<double, 3>,  3>);
static_assert(!fixed_container<std::span<double, 3>,  4>);
static_assert( fixed_container<double[3],             3>);
static_assert(!fixed_container<double[3],             4>);

Any ideas?

EDIT: The block

   requires (T const& t)
   {
       {std::size(t)} -> std::same_as<size_t>;
       {std::begin(t)};
       {std::end(t)};
   }

can be replaced by a single line

   std::ranges::sized_range<T>

I would like to have a concept that checks if a type is a container of fixed length. The type could be a C-style array, a std::array, a std::span or a custom-made container. I can check for the container condition as shown in the code below, but I cannot get it to check for the length condition.

template <typename T, size_t N>
concept fixed_container =
   requires (T const& t)
   {
       {std::size(t)} -> std::same_as<size_t>;
       {std::begin(t)};
       {std::end(t)};
   }
   // && std::declval<T>().size() == N // Error: 'declval() must not be used!'
   // && T::size() == N // Error: 'call to non-static member function without an object argument'
   ;

// These should all be correct
static_assert( fixed_container<std::array<double, 3>, 3>);
static_assert(!fixed_container<std::array<double, 3>, 4>);
static_assert( fixed_container<std::span<double, 3>,  3>);
static_assert(!fixed_container<std::span<double, 3>,  4>);
static_assert( fixed_container<double[3],             3>);
static_assert(!fixed_container<double[3],             4>);

Any ideas?

EDIT: The block

   requires (T const& t)
   {
       {std::size(t)} -> std::same_as<size_t>;
       {std::begin(t)};
       {std::end(t)};
   }

can be replaced by a single line

   std::ranges::sized_range<T>
Share Improve this question edited yesterday Arjonais asked Feb 7 at 7:19 ArjonaisArjonais 7355 silver badges21 bronze badges 5
  • maybe dup of stackoverflow.com/questions/70482497/… – 康桓瑋 Commented Feb 7 at 7:22
  • It's different, I want to check compile-time constantness AND actual value of size(). – Arjonais Commented Feb 7 at 7:24
  • 1 yap, but it can be easily modified to the concept you want: godbolt.org/z/4s4K7bn95 – 康桓瑋 Commented Feb 7 at 7:56
  • @康桓瑋 That'd get my vote if you put it in an answer – Ted Lyngmo Commented Feb 7 at 8:07
  • 1 Aside: using std::ranges::sized_range instead of the requires helps with constraint normalisation – Caleth Commented Feb 7 at 11:03
Add a comment  | 

2 Answers 2

Reset to default 7

[...] but I cannot get it to check for the length condition.

You'll need to handle it separately. For example, you might do the following (Which is inspired by a similar question mentioned in the comments):

template<typename T>
constexpr std::size_t get_fixed_size()
{
   if constexpr (std::is_array_v<T>)            return std::extent_v<T>;
   else if constexpr (requires { T::extent; })  return T::extent; // For std::span
   else if constexpr (requires { std::tuple_size<T>::value; })  
         return std::tuple_size<T>::value;   // For std::array
   else  return 0;
}

template<typename T, std::size_t N>
concept fixed_container = requires(T const& t)
{
   { std::size(t) } -> std::same_as<std::size_t>;
   { std::begin(t) };
   { std::end(t) };
}
&& get_fixed_size<std::remove_cvref_t<T>>() == N;

See live demo

It is much simpler than previous answers. Just test each type you want to support:

template <typename T, size_t N>
concept fixed_container = std::ranges::sized_range<T> &&
                        ( std::extent_v<T> == N || // var[N]
                          T::extent == N || // std::span
                          std::tuple_size<T>::value == N ); // Tuple-like types, i.e. std::array
发布评论

评论列表(0)

  1. 暂无评论