4

If I have a normal class I can "inject" a non-free friend function inside the class. (That among other things can be only be found by ADL).

case 1:

class A{
  double p_;
  friend double f(A const& a){return a.p_;}
};

If instead this is a template class I can do:

case 2:

template<class T>
class A{
  double p_;
  friend double f(A const& a){return a.p_;} // apparently A const& is a synomyn for A<T> const&
};

Now suppose that I need to implement f in terms of a class that needs to be defined later. I such case I tried doing this:

case 3:

template<class T>
class A{
    double p_;
    friend double f(A const& a);
};
...

This already gives a warning: "warning: friend declaration ‘double f(const A&)’ declares a non-template function [-Wnon-template-friend]".

Following the advice from the compiler I can do this:

template<class T> class A;

template<class T> double f(A<T> const& a);

template<class T>
class A{
    double p_;
    friend double f<>(A const& a);
};

template<class T> double f(A<T> const& a){return a.p_;}

Which requires so much more code and I am not even sure it is 100% equivalent to case 2 abov which is what I want, because now I have a truly free function that happens to be a friend instead of an injected friend.

Can case 3 be modified to be 100% equivalent to case 2 and still have a definition of f outside the class? In other words can one inject a friend function that is defined out of the class?


I tried this also, which gives a compiler error:

template<class T>
class A{
    double p_;
    friend double f(A<T> const& a);
};

template<class T> double A<T>::f(A<T> const& a){return a.p_;}

This answer finds the same solution but doesn't answer the question about case 3 being equivalent to case 2. What is the right way to write friend function declarations in template class?

alfC
  • 14,261
  • 4
  • 67
  • 118
  • 1
    *"A const& is a synomyn for A const&"*: Look at [injected class name](https://en.cppreference.com/w/cpp/language/injected-class-name) – Jarod42 Oct 10 '18 at 22:32

1 Answers1

6

Friend functions have special visibility rule (special case for ADL), so defining function outside the class is different than inside anyway.

Moreover, in case 2, the function is not template. even if you have one for every template. So to implement it outside of the class, you would have to implement each friend double f(A<T> const& a); for every T.

The advice is the most close workaround:

  • Your function (only the specialization) is friend.
  • but your function is template (so deduction should occurs:
    with friend double f(A<T> const& a, T); (case 2), f(A<float>{}, 42); would succeed
    whereas friend double f<>(A<T> const& a, T); would not
    (T would be float for A<float> and int for 42))

  • your function is declared outside, so its visibility is "different".

Now suppose that I need to implement f in terms of a class that needs to be defined later. I such case I tried doing this:

Other work around is to declare a private method which will do the job, that allow you to have friend definition inside the class. That private method can then be defined later:

template<class T>
class A{
    double p_;

    double do_f() const;
    friend double f(A const& a){return a.do_f();}
};

// Thing needed by A<T>::do_f

template<class T>
double A<T>::do_f() const
{
    // ...
}

If the return type is an incomplete type you have to do a trick with auto return (this works in g++11 and clang++11).

template<class T> class A;
class B;

template<class T>
class A{
    B do_f() const;
    friend auto f(A const& a){return a.do_f();} // not friend B f(...
};

class B{};

template<class T> B A<T>::do_f() const{return B{};}

int main(){A<double> a; f(a);}
alfC
  • 14,261
  • 4
  • 67
  • 118
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • So, there is no way to make case 3 equivalent to case 2? – alfC Oct 10 '18 at 22:30
  • 1
    No, but there are methods to stay in case 2. – Jarod42 Oct 10 '18 at 22:34
  • Then I don't understand what you are proposing. Can you please rewrite case 2 following your advice so I can see an example? – alfC Oct 10 '18 at 22:39
  • @alfC: Added example to stay with *inlined* defined friend function. – Jarod42 Oct 10 '18 at 22:46
  • Ah, excellent. So it seems to be good to make the friend functions "self contained" and at worst delegate to a member function. I didn't say it in the question but I have an extra problem the class that will be defined later is the return type of the friend. I am sure that I cannot return a class that is not defined from the defintion of the friend function (`class B` instead of `double` here). – alfC Oct 10 '18 at 22:50
  • 1
    I tried `B do_f() const; friend B f(A const& a){return a.do_f();}` and indeed got issues about incomplete type `B`, but `B do_f() const; friend auto f(A const& a){return a.do_f();}` succeed with both gcc/clang. – Jarod42 Oct 10 '18 at 22:58
  • that's right for me as well, it works with `auto f(...)` but not with `B f(...)`! I don't know why it works but it does. (good luck with C++98 then). i added this case to your answer if you don't mind. – alfC Oct 10 '18 at 23:07
  • Note sure it is valid though. – Jarod42 Oct 10 '18 at 23:07
  • I am not sure either. Intuition tells me it shouldn't work. Perhaps it works because the `auto` is deduced much later when the template is instantiated. If it is not valid, I am back to square 1 (square 0 in C++) for the use case I had in mind. – alfC Oct 10 '18 at 23:09
  • It is ironic that it is easier to define a member function out-of-line than a friend function. – alfC Oct 10 '18 at 23:24
  • 1
    Asked [incomplete-class-usage-with-auto-in-template-class](https://stackoverflow.com/questions/52750143/incomplete-class-usage-with-auto-in-template-class) to know validity of `auto`. – Jarod42 Oct 11 '18 at 00:02
  • with the years I found compilers (`nvcc`, `intel`) for which `auto` friend will choke SFINAE (the function is not detected). That is one problem of auto friends. I think this happens when the compiler cannot deduce auto in the "first pass". – alfC Mar 17 '21 at 20:38