如何在友元函数模板的类内定义中避免重定义错误?

时间:2012-02-17 08:49:02

标签: c++ templates friend function-templates class-template

考虑以下代码:

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() {}
};
*/

现在编译好(ideone)。但如果我取消注释B的定义,则会出现以下错误(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

我知道(嗯,我想我知道)之所以会出现这个错误。

所以我的问题是:

如果在友元函数模板的类内定义的情况下如何避免重定义错误?

只要我在类中提供主模板(非专业化)的定义,我就会收到此错误。以这种方式定义主模板还有另一个问题:它使f类模板的所有实例化的friend函数模板Base的所有实例化,我也想避免。如果f<T>Base<T>不相同,我想让f<U>成为Base<T>的朋友,而不是U T的朋友。同时,我也想在课堂内提供定义。有可能吗?

2 个答案:

答案 0 :(得分:4)

你真的需要在课堂上定义f吗?如果您在外面定义它,您的问题就会消失,您也可以强制执行所需的一对一关系(即只有f<T>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; 
};

但请注意,只有f<T>Base<T>的朋友的事实不会阻止以下代码编译:

B b;
f<A>(&b); // compiles, f<A> calls Base<A>::run, but the cast is wrong

答案 1 :(得分:1)

友元函数是一个全局函数,即使您将其实现放入任何类的主体中也是如此。问题是,当您实例化Base<T>两次(在任何上下文中)时,您提供了两个f的实现。请注意,f 依赖于T,并且无法使用T;对于所有Base<T>来说,它都是相同的功能。

一个简单的解决方案是只在类模板中提供f的声明并在其外部实现:

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() {
}

上面的代码用我的g ++编译