在朋友功能模板的类定义的情况下,如何避免重新定义错误?

Naw*_*waz 2 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() {}
};
*/
Run Code Online (Sandbox Code Playgroud)

现在可以编译良好(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
Run Code Online (Sandbox Code Playgroud)

我知道(嗯,我想我知道)它导致此错误的原因。

所以我的问题是:

在朋友功能模板的类定义的情况下,如何避免重新定义错误?

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

Luc*_*lle 5

您是否真的需要定义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; 
};
Run Code Online (Sandbox Code Playgroud)

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

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