Access private members in requires clause

Viewed 58

Consider the following program:

#include <iostream>

template<typename T> void f1(T& v)
{
  std::cout << "f1: can call g" << std::endl;
  v.g();
}

template<typename T> void f2(T& v) requires requires (T& v) { v.g(); }
{
  std::cout << "f2: can call g" << std::endl;
  v.g();
}

template<typename T> void f2(T&) requires (!requires (T& v) { v.g(); })
{
  std::cout << "f2: cannot call g" << std::endl;
}

class A
{

public: // if commented out, f2 will not call g anymore

  void g()
  {
    std::cout << "g called" << std::endl;
  }

  template<typename T> friend void f1(T& v);
  template<typename T> friend void f2(T& v);
};

class B
{
};


int main()
{
  std::cout << "A" << std::endl;
  A a{};
  f1(a);
  f2(a);

  std::cout << "B" << std::endl;
  B b{};
  f2(b);

  return 0;
}

The function g might exists in a class or not. If it does (like for A), the function f2 should call it, if not (like for B) it shouldn't. This distinction is made via the requires clause.

I befriended f2 in A so it should be able to call g even if it is private (in general befriending works fine, see f1). However, the requires seems to ignore that the function is befriended and thus, when making g private in A, g is not called anymore.

Why is this? Is there a workaround for this, i.e. conditioning on whether a function can be called, even if it's private (but befriended)? Maybe even using old school std::enable_if?

1 Answers

When you friend a function or function template, it's only the body of that function that gains access - not its various accoutrements. The constraints attached to it are extraneous.

You can fix this by moving the check inside the body of f2, so that it's checked in a context that has this access:

template <typename T>
void f2(T& v)
{
    if constexpr (requires { v.g(); }) {
        std::cout << "f2: can call g\n";
        v.g();
    } else {
        std::cout << "f2: cannot call g\n";
    }
}
Related