在模板类中声明非模板函数?

Declare non-template function in a template class?

本文关键字:函数 声明      更新时间:2023-10-16

注意:这篇文章与这篇文章不同:为类外的模板类声明非模板友元函数,所以请在将其标记为重复之前阅读我的问题。

我想在类模板中声明一个非模板友元函数,并且该友元函数的参数和返回类型与模板参数无关。我应该怎么做?

请注意,它与上一个问题不同,因为在该问题中,该 friend 函数的参数和返回类型与模板参数相关

示例,改编自上面的问题:

// class.h
#include <iostream>
using namespace std;
template <typename T>
struct B
{
T value;
int value2;
B() : value2(1) {}
friend void modify(const int&); // unrelated to T!
void printValue2() {
modify(value2);
cout << value2 << endl;
}   
};
// define friend function foo() in a class.cpp file, not in the header
void modify(const int &v) { v = v * 2 + 1; } // HOW should I write it?
// main.cpp
int main() {
B<int> b;
b.printValue2();
return 0;
}

我知道我可以在这个模板类之外声明modify(),所以它成为一个普通的普通函数。但是我希望只有这个模板类可以访问modify().或者,为了实现访问控制的目标,我可以在此模板类中将modify()定义为静态方法,但这会使该方法成为模板方法,迫使我在标头中定义它。

追问:如果上面的好友方式行不通,我该如何同时实现这两个目标:

  • 访问控制:只有该类模板可以访问modify()
  • 能够在 *.cpp 文件中定义modify(),而不是在标头中定义。

接受的答案

为了实现上述两个目标,不要滥用友谊。

最佳做法是让类模板私下继承非模板基类,并在该基类中声明与模板参数无关的常见非模板方法

因此,您可以在单独的 *.cpp 文件中定义这些方法,从而减小标头的大小。

您可以使用私有继承而不是友谊:

// class.h
#include <iostream>
class B_helper
{
protected:
static void modify(int &v);
};
template <typename T>
struct B : private B_helper
{
T value;
int value2;
B() : value2(1) {}
void printValue2() {
modify(value2);
std::cout << value2 << std::endl;
}   
};
// class.cpp
void B_helper::modify(int &v) { v = v * 2 + 1; }

你这样做是这样的:

// class.cpp
void modify(const int &v) { v = v * 2 + 1; }

你实际上是在滥用友谊,但没关系。这意味着您需要解决使用friend声明函数的含义:它只能通过 ADL 可见!现在没有办法引用modify,因为modify不依赖于B,所以B的作用域从来不会搜索名为modify的函数。

有一个解决方法,但它并不漂亮。您需要在每个使用它的函数中声明modify。您也可以在全局范围内声明它,但每个人都可以调用它。或者,您始终可以在detail命名空间中声明它(但这有一点相同的问题(:

template<typename T>
void B<T>::printValue2() {
void modify(const int&);
modify(value2);
cout << value2 << endl;
}

正如我在评论中所说,friend船控制对类的访问。只要您的函数modify()是一个独立的函数,它就不能成为好友。由于要从模板调用它,因此也不能将其隐藏在.cpp文件中,但必须与类模板 B 及其成员的定义一起使用modify()可见。

一种解决方案是将modify作为static方法放在辅助非模板类中,这反过来又与模板B<>交朋友。

// file foo.h (header)
namespace foo {
template<typename> class B;            // forward declaration
class exclusive_for_B
{
template<typename T>
friend class B<T>;
static void modify(int&x)          // must take int&, not const int&
{ x &= x+42; }
};
template<typename T>
class B
{
int val;
public:
...
void printvalue()
{
exclusive_for_B::modify(val);  // access via friendship
std::cout << val << 'n';
}
};
}