3

I was learning about non-template friend function & template friend function to a templated class. So I tried the code below :

#include <iostream>   


template<typename T>
class  cl
{
private :
    T val;
public:
    cl()= default;
    explicit cl(T v) : val(std::move(v)) {}    

    friend void non_template_friend(cl m);
};

template <typename T>
void non_template_friend(cl<T> m)  { std::cout << m.val << std::endl;}


int main()
{

    cl<int> c(10);
    non_template_friend(c);
    return 0;
}

so when I compile I got : undefined reference tonon_template_friend(cl)' ` So to resolve that I have to move the friend function definition inside the class definition like so :

template<typename T>
class  cl
{
private :
    T val;
public:
    cl()= default;
    explicit cl(T v) : val(std::move(v)) {}    

    friend void non_template_friend(cl m) { std::cout << m.val << std::endl;}
};

But I was wondering,is there any trick to do to be able to define the friend fuinction outside the class definition ?

Thank you.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
Blood-HaZaRd
  • 2,049
  • 2
  • 20
  • 43
  • 1
    Related to [what-is-the-right-way-to-define-a-friend-function-outside-a-template-class](https://stackoverflow.com/questions/52749225/what-is-the-right-way-to-define-a-friend-function-outside-a-template-class) – Jarod42 Jan 24 '20 at 19:43

2 Answers2

5

If you want the definition outside the class, it needs to be a template function since it must be able to accept any kind of c1.

You can achieve this by forward declaring both the class and the function.

#include <iostream>

template<typename T>
class  cl;

template<typename T>
void non_template_friend(cl<T> m);

template<typename T>
class  cl
{
private :
    T val;
public:
    cl()= default;
    explicit cl(T v) : val(std::move(v)) {}    

    friend void non_template_friend<T>(cl m); //Now we can refer to a specific instatiation of the template  here
};

template <typename T>
void non_template_friend(cl<T> m)  { std::cout << m.val << std::endl;}


int main()
{

    cl<int> c(10);
    non_template_friend(c);
    return 0;
}
super
  • 12,335
  • 2
  • 19
  • 29
  • No problem. It's a bit ugly but it gets the job done. AFAIK there is no cleaner way to do this. – super Jan 24 '20 at 19:26
  • I have a question; in C++ inSights : the generated code shows two definitions of the same specialized friend function. How could be having 2 same definitions ? – Blood-HaZaRd Jan 24 '20 at 19:34
  • 1
    I can't answer why, but I know that the compiler is allowed to have several definitions of the same template. Usually one from each TU that uses it. The linker will then discard all of them but one and assume they are identical (if they are not, that's UB). Same thing that happens with `inline`. – super Jan 24 '20 at 19:38
  • `template void non_template_friend(..)`. name seems wrong now... – Jarod42 Jan 24 '20 at 20:26
0

In the first program

template<typename T>
class  cl
{
private :
    T val;
public:
    cl()= default;
    explicit cl(T v) : val(std::move(v)) {}    

    friend void non_template_friend(cl m);
};

template <typename T>
void non_template_friend(cl<T> m)  { std::cout << m.val << std::endl;}

You declared a non-template friend funcrion but then you declared and defined a template function with the same name/

A non-template friend function declaration means thatf you have to provide a set of overloaded non-template functions for specializations of the template class/

Here is a demonstrative program.

#include <iostream>
template<typename T>
class  cl
{
private :
    T val;
public:
    cl()= default;
    explicit cl(T v) : val(std::move(v)) {}    

    friend void non_template_friend(cl m) ;
};

void non_template_friend( cl<int> m)  { std::cout << m.val << std::endl;}
void non_template_friend( cl<double> m)  { std::cout << m.val << std::endl;}

int main() 
{
    cl<int> c(10);
    non_template_friend(c);

    cl<double> c2( 20.2 );
    non_template_friend(c2);


    return 0;
}

The program output is

10
20.2

That is in a template class a non-template friend function declaration is in fact declares a set of overloaded non-template functions.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • so if I understand the concept, if i want to make friend a non-templated function to a templated class, I have 1) write the definition inside the class or 2) Declare it inside and forced to define all specialized versions of it. – Blood-HaZaRd Jan 24 '20 at 19:42
  • @Blood-HaZaRd There is no need to declare non-template friend functions for all specialization of a class. It is enough to declare only those non-template friend functions that will be used in the program. If you will define a non-template friend function in a class then you will have only one the same definition/ That is you will be unable to "specialize" non-template friend functions. – Vlad from Moscow Jan 24 '20 at 19:46
  • yeah that what i meant :) only for the used types ^ ^. – Blood-HaZaRd Jan 24 '20 at 19:47
  • @Blood-HaZaRd The answer you accepted as the best does not refer to non-template friend functions. In his example he declared a template friend function.. It is not the same as a non-template friend function. – Vlad from Moscow Jan 24 '20 at 19:52
  • @VladfromMoscow Seems like a bit nit-picking. Isn't a specific instatiation of a template just a regular function? The friend is not the template, but one and only one instatiation of it. – super Jan 24 '20 at 20:52
  • @super For each specialization of the class there is used a specialization of the template function. – Vlad from Moscow Jan 24 '20 at 20:56
  • @VladfromMoscow There are no specializations at all in the example, only instatiations of the templates. Even though you could add specializations to either one of them if needed. But my question was can you really say that a specific instatiation of a template function is anything other then a function. It's no longer a template, right? – super Jan 24 '20 at 20:59
  • @super In this declaration non_template_friend there is used a simple template-id. It may not be used with non-template functions. – Vlad from Moscow Jan 24 '20 at 21:01
  • @super I answered your question. You are using a template function and its specializations specified by the simple-template-id are friends of the template class These specializations are instantiated from the template function definition. You can even declare a separate specialization of the template function and it will be a friend.. – Vlad from Moscow Jan 24 '20 at 21:05
  • @VladfromMoscow No, that's the whole point of only making an instatiation of the template a friend. `non_template_friend` is not a friend of `c1` for example. – super Jan 24 '20 at 21:14
  • @super The specialization non_template_friend is a friend of the specialization cl – Vlad from Moscow Jan 24 '20 at 21:17
  • @super To make it clear declare a non-template function for example void non_template_friend( cl ). And not define it. Then in main write cl c( 10 ); non_template_friend( c ); And the compiler will issue an error that the function is not defined because the compiler selected indeed the non template function instead of the corresponding template specialization. – Vlad from Moscow Jan 24 '20 at 21:20
  • @VladfromMoscow `non_template_friend` is a template. `non_template_friend` is a function. It seems to me that saying *In his example he declared a template friend function* is not really strictly true. It's more twisting and turning terminology. – super Jan 24 '20 at 21:29
  • @super no _template_friend is a specialization of the template function. The compiler will not even instantiate it if the specialization is not used. As I said you may to declare indeed a non template function with the same name and the same type of the parameter as the specialization and the compiler will select the non template function and say that it is not a friend.:) – Vlad from Moscow Jan 24 '20 at 21:33
  • I agree with super that when I posted my question and due to the fact that I am learning templates I may miss-confused him with the function name. So as Vlad said, I declares 2 functions, 1 non template and a second templated. and agree coz in C++ Insight, I got 2 fuunctions non-template one and a non-template And using Vlad code I don't have . – Blood-HaZaRd Jan 24 '20 at 21:37
  • @VladfromMoscow Yes, since that would be 2 different functions, and overload resolution would prefer the non-template one if it's an exact match. I don't really see how that is relevant. – super Jan 24 '20 at 21:41
  • @super What?! Let repeat your own phrase "overload resolution would prefer the non-template one". Do you understand what you said? Between what kinds of functions the compiler selects? – Vlad from Moscow Jan 24 '20 at 21:48
  • @VladfromMoscow Yes. From main we are calling `non_template_friend(c);`. That can refer to either the template `non_template_friend` or the non-template function with the same name. That still does not change the fact that when we refer to `non_template_friend` we are no longer refering to the template, we are refering to a function. It could be instatiated from the base template or it could use a specialization if one exists. And that leaves me thinking that saying that we have a *template friend function* is still ambiguous at best, but more accurately incorrect. – super Jan 24 '20 at 21:53
  • 1
    @super for example let's assume that we have two functions template void f( T t ); and void f( int ); if we want to call the template function for an argument of the type int we have to write at least f<>( x ); If we just write f( x ); then the non-template function is called. So we are using terms "template function" and "non-template function". For this call f<>( x ) we are saying that the template function is called. – Vlad from Moscow Jan 24 '20 at 21:58
  • @VladfromMoscow So you mean that you objection that *It is not the same as a non-template friend function* only has to do with teminology? Or is there an actual difference between the two in behaviour from a language stand-point? – super Jan 24 '20 at 22:10
  • @super There is a big difference. For specializations of a template function the primary template function declaration must exists. The compiler will not instantiate a template specialization if it is not used. For the code you provided in your answer each template specialization is a friend of the corresponding specialization of the class template. And the compiler will itself generate a required specialization if this is needed. For a non-template function only non-template functions that was explicitly defined by the user are friends of the class template. – Vlad from Moscow Jan 24 '20 at 22:21