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