有没有办法在 require 表达式中使用 using 声明

ois*_*syn 25 c++ c++-concepts c++20 requires-expression

我想测试一个类型是否可以传递给某个函数,但我想在函数查找上使用 ADL 并包含来自某个命名空间的函数。

考虑这段代码:

#include <utility>
#include <vector>

template<class T>
concept Swappable = requires(T& a, T& b)
{
    swap(a,b);
};

static_assert(Swappable<std::vector<int>>); // #1
static_assert(Swappable<int>); // #2
Run Code Online (Sandbox Code Playgroud)

#1 成功,它发现std::swapbecausestd是 的关联命名空间std::vector<int>。但 #2 失败了,内置类型没有关联的命名空间。

我该怎么写这样的东西:

template<class T>
concept Swappable = requires(T& a, T& b)
{
    using std::swap; // illegal
    swap(a,b);
};
Run Code Online (Sandbox Code Playgroud)

AFAIK,您不允许在 require 表达式中使用 using 声明。

(注意,虽然对此有一个完美的标准 C++ 概念,但std::swappable此示例swap仅用于说明。我并不是特别想测试某些东西是否实际上是可交换的,我只是想找到一种方法来实现这样的概念其中自定义函数在已知命名空间中具有默认实现,但可能在关联的命名空间中具有重载。

编辑作为一种解决方法,我可以在一个单独的名称空间中实现这个概念,其中的名称被拉入。对此不太满意,但它有效。

namespace detail
{
    using std::swap;

    template<class T>
    concept Swappable = requires(T& a, T& b)
    {
        swap(a,b);
    };
}

// and then either use it
using detail::Swappable;

// or redefine it
template<class T>
concept Swappable = detail::Swappable<T>;
Run Code Online (Sandbox Code Playgroud)

Art*_*yer 23

您可以将其放入 lambda 中:

template<class T>
concept Swappable = []{
    using std::swap;
    return requires(T& a, T& b) { swap(a, b); };
}();
Run Code Online (Sandbox Code Playgroud)

  • 不错,很酷的技巧!我尝试使用 lambda 来解决我的问题,但是需要表达式不会检查它们的主体,其中的任何错误都只是硬编译器错误。但我没有考虑从 lambda 返回 require 表达式! (5认同)

Nic*_*las 5

避免使用旧using的习语。相反,请使用自定义点等效项,例如ranges::swap.

也就是说,您不应要求用户using在其代码中使用基于 - 的习惯用法。提供一个可以执行所需操作的自定义点对象。可以限制重载operator()/模板来创建习惯用法的效果,using而不需要用户实际调用using.

ranges::swap这是如何完成此操作的一个很好的例子。

  • @oisyn:“*从可读性的角度来看,这只是糟糕的 C++ 代码。*”并且到处乱扔一堆 `using` 指令*不是吗?`ranges::swap(...)` 比 `using std::swap; 更具可读性。交换(...)`。更好的是,它可以用在没有“using”指令的地方。就像...概念;) (3认同)
  • @Artyer:我并没有说这就是算法的全部。显然,它还需要检查成员“swap”,然后检查类型是否可移动/可构造等。重点是,您可以执行“using std::swap”所做的所有操作,而无需实际执行那。 (2认同)