如何为成员函数声明一个概念并在友元函数中使用

How to declare a concept for the member function and use it in the friend function

我来到下面这个使用概念的例子,它检查成员函数是否存在于 class 中,然后在友元函数中使用这个概念。例如,为一堆类似的 classes

重载 operator<<
#include <iostream>
#include <vector>

template<class T>
concept Sizable = requires(T a)
{
    {a.size()} -> std::same_as<size_t>;
};

template<class T> requires Sizable<T>
std::ostream& operator<<(std::ostream& os, const T& a);

template<class T>
class A {
    std::vector<T> storage;
public:
    size_t size() const { return storage.size();}
    friend std::ostream& operator<< <A<T>>(std::ostream& os, const A<T>& a);
};

template<class T> requires Sizable<T>
std::ostream& operator<<(std::ostream& os, const T& a)
{
    for (size_t i = 0; i < a.size(); ++i) {
    }
    return os;
}

int main()
{
    A<int> a;
    operator<< <A<int>>(std::cout, a);
}

但是由于我们有成员访问不完整类型的原因,此代码无法编译。

考虑到我们无法转发声明概念,有什么办法可以实现这一目标吗?

如果朋友定义被移动到 class 中,那么代码可以编译并运行。

概念做不到这一点。它们是函数模板声明的一部分,因此在您与 A<T> 的特化成为朋友时会进行检查。问题在于,在 friend 声明点,class 未被视为完全定义。事实上,它仅在非常特定的地方才被认为是在其自己的定义中完全定义的:

  • 函数体,
  • 默认参数,
  • noexcept 说明符,
  • 默认成员初始值设定项。

好友声明是其中的 none 个。所以要引用 class 是指一个不完整的类型。该概念不是 class 的一部分,因此它无法“看到”“先前声明的”部分。一旦你 instate A<T> 和 friend 声明,这个概念必须被验证,因为 operator<< <A<T>> 的声明被实例化了。鉴于它应用于不完整的类型,概念检查失败。

即使这不是问题,概念检查也会使友谊在很大程度上变得毫无意义。只有当它们检查的成员 public 并且明确时,概念才能得到满足。因此,如果 friend 函数需要访问任何私有或受保护的成员,那么概念将无济于事。为了 真正 保持概念检查,我们将只需要使用 class 的 public 部分......为此我们不需要与任何东西成为朋友.确实,去掉友元声明可以使您的代码合式。

如果我们打算改用 class 的私有部分,那么我们不能用概念检查它,必须将检查推迟到 operator<< 的主体的实例化就像模板“正常”工作一样。我认为在那种情况下“验证 public 接口”的概念是没有实际意义的(因为我们以后仍然会失败)。在这种情况下,我会完全放弃这个概念。