首页 > 解决方案 > 为具有返回约束值的成员函数的对象指定概念

问题描述

我试图通过移植一些旧代码来围绕 C++ 20 概念和约束。

struct Status
{
    std::string status;
    std::time_t statusDate;
};

struct CurrentStatusStack
{
    std::vector<Status> statusVec;
    std::vector<std::filesystem::path> ticketPathVec;
};

void setBar(CurrentStatusStack s)
{
    ui->setLatestStatusMessage(s.statusVec.back().status);
    ui->setLatestStatusMessagePath(s.ticketPathVec.back());
}

为了将上面的代码块翻译成一个类似但通用的函数setFoo(const T& t),并确保T类型实现了setBar需要的函数,我写了一些概念:

  1. 一个ContainerOf概念
/*
  `ContainerOf` requires container (could be a vector or a set
  or other STL-like containers) to implement at least:
  1. `cbegin()` that returns a const iterator pointer to the first element.
  2. `empty()` an emptiness check fn.
*/
template <class Container, typename T>
concept ContainerOf = requires(Container a, T)
{
  requires std::regular<Container>;
  requires std::same_as<typename Container::value_type, T>;
  {
    a.cbegin()
    } -> std::same_as<typename Container::const_iterator>;
  {
    a.empty()
    } -> std::same_as<bool>;
};
  1. 一个HasTicketPaths概念
/*
  `HasTicketPaths` ensures that that structure given implements a `getTicketPaths`
  function that returns a container that satisfies `ContainerOf` with
  `std::filesystem::path` elements in it.
 */
template <typename T>
concept HasTicketPaths = requires(T t)
{
  {
    t.getTicketPaths()
    } -> ContainerOf<fs::path>;
};
  1. 一个IsStatus概念
/*
  To set status the obj needs at least two elements:
  1. Status string and
  2. the date the status was posted,
  `IsStatus` ensure those constrients by requiring `status` and
  `statusDate` functions that return a `std::string` and `std::time_t`
  respectively.
*/
template <typename T>
concept IsStatus = requires(T t)
{
  {
    t.status()
    } -> std::same_as<std::string>;
  {
    t.statusDate()
    } -> std::same_as<std::time_t>;
};

现在我认为所有要做的就是以某种方式在概念上结合这两个概念HasStatus并将函数原型更改为

template <typename T>
requires HasStatus<T> && requires HasTicketPaths<T>
void setFoo(const T& t);

但我不知道该怎么做。

我想象它看起来像这样

/*
  `HasStatus` ensures that the container has a `getStatus` function that return
  a container with each element type ensuring `IsStatus`'s requirement.
 */
template <typename T>
concept HasStatus = requires(T t)
{
  {
    t.getStatus()
  } -> ContainerOf<IsStatus>;
};

但它会产生以下错误

invalid use of ‘decltype(auto) [requires ::IsStatus<<placeholder>, >]’ in template argument
   70 |   } -> ContainerOf<IsStatus>;

我想我误解了concepts实际的工作方式,但我不确定在哪里/寻找什么。

标签: c++c++20c++-concepts

解决方案


概念不是类型,因此它不能作为容器元素类型出现——既不能出现在对象的类型中(这就是你必须使用std::vector<std::any>近似的原因std::vector<std::copyable>),也不能出现在你的概念类型中ContainerOf。此外,您不能将概念用作模板参数,因此您不能拥有更高阶的ContainerLike概念。

您可以做的是创建一个Container仅检查的概念empty,添加约束

{ *c.cbegin() } -> IsStatus;

并将其应用于t.getStatus()另一个概念。


推荐阅读