Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 在类内定义友元函数模板时,如何避免重新定义错误?_C++_Templates_Friend_Function Templates_Class Template - Fatal编程技术网

C++ 在类内定义友元函数模板时,如何避免重新定义错误?

C++ 在类内定义友元函数模板时,如何避免重新定义错误?,c++,templates,friend,function-templates,class-template,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>

考虑以下代码:

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() {}
};
*/
我知道(嗯,我想我知道)它产生这个错误的原因

所以我的问题是:

在类内定义友元函数模板时,如何避免重新定义错误


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

朋友函数是一个全局函数,即使您将其实现放入任何类的主体中。问题在于,当您实例化
Base
两次(在任何上下文中)时,您提供了
f
的两个实现。注意,
f
不依赖于
T
,它不能使用
T
;所有
Base
的函数都是相同的

一个简单的解决方案是只在类模板内声明
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() {
}
模板
阶级基础
{
模板
朋友无效f(无效*ptr);
受保护的:
虚空运行()=0;
};
模板
无效f(无效*ptr){
静态_cast(ptr)->run();
}
A类:公共基地
{
受保护的:
虚拟空运行(){}
};
B类:公共基地
{
受保护的:
虚拟空运行(){}
};
int main(){
}

上面的代码是用我的g++

编译的,你真的需要在类中定义
f
?如果您在外部定义,您的问题就会消失,您还可以强制执行您想要的一对一关系(即,只有
f
Base
的朋友):

模板类库;
模板
无效f(无效*ptr){
静态_cast(ptr)->run();
}
模板
阶级基础
{
朋友void f(void*ptr);//只有一个实例是朋友
受保护的:
虚空运行()=0;
};
但是,请注意,只有
f
Base
的朋友这一事实不会阻止编译以下代码:

B b;
f<A>(&b); // compiles, f<A> calls Base<A>::run, but the cast is wrong
B;
餐饮部;//编译,f调用Base::run,但强制转换错误

我看不出编译器会在这方面出错的原因,而且似乎clang的人也看不到原因,当使用clang.GCC和MSVC10编译时,如果我取消注释
B
@plasmah的定义,这两个函数都会给出错误:因为友元函数不是成员函数,因此可以不依赖于类模板的参数。我知道它可以工作(我已经尝试过了),我在我的帖子中说我不想这样做。根据你提供的限制,我认为你必须这样做,因为我在第一段中解释了这个原因。我只能看到一个有效的理由来肯定地回答“你真的需要在类中定义
f
”吗:需要排除对
f
参数类型的隐式转换。当在类中定义friend函数时,只能通过ADL找到它,否则可以通过正常查找找到它,这可能(也可能不是)不需要。有关更多信息,请参见。请注意,必须将
f
定义为
inline
,才能使此代码等效于类内定义,这是隐式的
inline
(C++11§11.3/7,C++17§14.3/7)。在
内联
模板和非内联
模板之间存在差异。
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; 
};
B b;
f<A>(&b); // compiles, f<A> calls Base<A>::run, but the cast is wrong