4

Consider this code:

template<typename T>
class Base
{
   template<typename U>
   friend void f(void *ptr) {
     static_cast<Base<U>*>(ptr)->run();
   }
   protected:
       virtual void run() = 0; 
};
 
class A : public Base<A>
{
   protected:
       virtual void run() {}
};
 
/*
class B : public Base<B>
{
   protected:
       virtual void run() {}
};
*/

It compiles fine now (ideone). But if I uncomment the definition of B, then it gives the following error (ideone):

prog.cpp: In instantiation of ‘Base<B>’:
prog.cpp:20:   instantiated from here
prog.cpp:6: error: redefinition of ‘template<class U> void f(void*)’
prog.cpp:6: error: ‘template<class U> void f(void*)’ previously defined here

I know (well,I think I know) the reason why it gives this error.

So my question is :

How to avoid redefinition error in case of in-class definition of friend function template?

As long as I provide the definition of the primary template (not specialization) inside the class, I will get this error. There is also another problem with defining primary template in this way: it makes all instantiations of f function template friend of all instantiations of Base class template, which I also would like to avoid. I want to make f<T> a friend of Base<T> but not f<U> a friend of Base<T> if U and T are not same. At the same time, I also want to provide the definition inside the class. Is it possible?

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • I see no reason why the compiler should error out on that, and it seems the clang people don't see a reason too, as it compiles with clang. – PlasmaHH Feb 17 '12 at 09:25
  • GCC and MSVC10 both give error if I uncomment the definition of `B`. – Nawaz Feb 17 '12 at 09:44
  • @PlasmaHH: Because a friend function is not a member function and therefore can be non-dependent of the class template's arguments. – Sebastian Mach Feb 17 '12 at 10:49

2 Answers2

5

Do you really need to define f into the class? If you define it outside, your problem disappears and you can also enforce the one-to-one relationship you want (i.e. only f<T> is a friend of Base<T>):

template <typename T> class Base;

template <typename U>
void f(void *ptr) {
   static_cast<Base<U>*>(ptr)->run();
}

template<typename T>
class Base
{
   friend void f<T>(void *ptr); //only one instanciation is a friend

   protected:
     virtual void run() = 0; 
};

However, note that the fact that only f<T> is a friend of Base<T> will not prevent the following code from compiling:

B b;
f<A>(&b); // compiles, f<A> calls Base<A>::run, but the cast is wrong
Luc Touraille
  • 79,925
  • 15
  • 92
  • 137
  • I can only see one valid reason to positively answer "Do you really need to define `f` into the class?": it is the need to rule out implicit transformations to `f` arguments types. When friend function is defined in-class it can be found only through ADL, otherwise it can be found through normal look up, which might (or might not) be undesirable. See [there](https://stackoverflow.com/a/52068027/1906174) for more. – Wormer Aug 15 '19 at 18:39
  • Please note that `f` must be defined as `inline` for this code to be equivalent for in-class definition, which is implicitly `inline` (C++11 §11.3/7, C++17 §14.3/7). There is a [difference](https://stackoverflow.com/a/10536588/1906174) between `inline` templates and not. – Wormer Aug 15 '19 at 18:47
1

A friend function is a global function, even if you put its implementation into the body of any class. The problem is that when you instantiate Base<T> twice (in any context) you provide two implementations of f. Note, that f does not depend on T, and it cannot use T; it's the same function for all Base<T>.

A simple solution is to provide only the declaration of f within the class template and implementation outside it:

template<typename T>
class Base
{
  template<typename U>
  friend void f(void *ptr);
  protected:
    virtual void run() = 0;
};


template<typename U>
void f(void *ptr) {
  static_cast<Base<U>*>(ptr)->run();
}

class A : public Base<A>
{
 protected:
   virtual void run() {}
};

class B : public Base<B>
{
protected:
  virtual void run() {}
};

int main() {
}

The above code compiles with my g++

CygnusX1
  • 20,968
  • 5
  • 65
  • 109
  • I know it works (I've tried it already), and I said it in my post that I do not want to do this. – Nawaz Feb 17 '12 at 10:36
  • By the constraints you provided, I think you _have_ to do that, for the reason I explained in the first paragraph. – CygnusX1 Feb 17 '12 at 10:39