如何使模板参数的构造函数成为友元?

How do I make a template parameter's constructor a friend?

在 C++11 中,他们可以简单地使用 friend T 将模板参数设为好友。您还可以使用 friend T::Method().

该参数内的朋友方法

但是,如何将模板参数的构造函数设为好友?

class BeMyFriend
{
public:
 BeMyFriend& operator=(const BeMyFriend& rhs) = default;
 BeMyFriend(const BeMyFriend& rhs) = default;
};

template<class T>
class Test
{
 friend T& T::operator=(const T&); //Works fine, no error
 friend T::T(const T&); //error: prototype for 'BeMyFriend::BeMyFriend(const BeMyFriend&)' does not match any in class 'BeMyFriend'
};

int main()
{
 Test<BeMyFriend> hmm;

 return 0;
}

我可以将模板参数的 operator= 设为好友,但我无法将 T::T(const T&).

设为好友

如何让 friend T::T(const T&); 工作?


编辑: 这似乎与 中解决的问题不同。那里的问题是处理声明中的循环模板参数。它不处理实际模板参数的构造函数。

Foo 的类型是一个普通的模板 class,而不是像我的示例中的 T 这样的模板参数。该提交中的 friend Foo<B>::Foo<B>() 之类的内容应该编译得很好,这与我在 friend T::T(const T&).

中遇到的问题不同

编辑: 如果这最终很重要,我正在使用 gcc 7.2 进行编译。

编辑: 我还想澄清一下,C++ 确实支持让构造函数成为朋友。例如,第一个示例中的 friend X::X(char), X::~X(); http://en.cppreference.com/w/cpp/language/friend.

这里的问题是如何使模板参数的构造函数成为友元。

不知何故,我认为 T::T(const T&) 没有被编译器很好地解析,因为它没有将成员构造函数 T() 之前的命名空间 T:: 视为对某些外部 class,显然可以在错误控制台 ISO C++ forbids declaration of ‘T’ with no typeprototype for ‘BeMyFriend::BeMyFriend(const BeMyFriend&)’ does not match any in class ‘BeMyFriend’ 中看到,其中编译器公然尝试从 class 外部导出任何定义或声明,因此 T:: 应该被用户强制引入编译器引用到友好的 class 像这样 T&:: 这足以消除歧义。

您可以检查 here 实例化器 "works" 是否完美并且该值已正确添加。

如果,尽管如此,您在此 example 中看到显示的错误 std::__cxx11::string Test<BeMyFriend>::mything’ is private within this context areyoumyfriend.mything; 描述了对私有值的违规访问状态,这是因为成员函数与主机不友好 class 简直了。

我认为 C++ 标准禁止您尝试做的事情。也许是 defect/oversight;也许是故意的。我不确定,所以我只列出我在标准 中找到的部分(浏览章节编号是安全的 unless/until 有人正在检查我的分析) :

  1. 在6.4.3.1.2中,有关于何时考虑命名构造函数的描述。这涉及 "injected-class-name" ,它(根据 12.2)表示插入 class 范围的 class 的名称。在你的上下文中,我读到这些部分是说要命名构造函数,你需要 T:: 后跟 class-name of T。你有 T::T,如果 T 被认为是 Tclass-name,它就可以工作。让我们看看结果如何。

  2. 在 15.1.1 中,声明您的 friend 声明需要命名一个构造函数。它接着说 class-name 不应是 typedef-name。所以我们应该没问题,只要 T 不是 typedef-name.

  3. 在 17.1.3 中,声明在您的 class Test 中,标识符 T 是一个 typedef-name。呃哦。

所以这有点奇怪。如果我没看错的话,你需要使用 friend T::BeMyFriend 来命名构造函数,这当然只适用于这个特定的例子。对于其他模板参数,这将查找名为 "BeMyFriend" 的成员函数。不是你要找的。

(您可能会注意到,在声明为友元的构造函数示例中,使用的 class 名称始终是定义 class 时使用的名称。在那种情况下,没有 typedef正在继续,所以不会出现这个问题。)

解决方案? 我认为你需要将 class T 加为好友,将 T 中的静态函数加为好友,然后从构造函数中调用它,或者找到一种(更好的?)方法来做你想做的事情而不需要使用朋友。当我看到一个模板参数成为朋友时,我的脑海里就飘起了一面警告旗——这是合法的,但常常违背封装原则。

构造函数是非常特殊的方法。他们没有 return 类型(甚至没有 void),也没有真实姓名。 AFAIK 不能用于朋友声明。

一种可能的解决方法是在 class BeMyFriend:

中构建复制工厂函数
class BeMyFriend
{
public:
 BeMyFriend& operator=(const BeMyFriend& rhs) = default;
 BeMyFriend(const BeMyFriend& rhs) = default;
 static BeMyFriend makeCopy(const BeMyFriend& rhs) {
    BeMyFriend tmp(rhs);
    return tmp;
 }
};

template<class T>
class Test
{
 friend T& T::operator=(const T&); //Works fine, no error
 //friend T::T(const T&); //error: prototype for 'BeMyFriend::BeMyFriend(const BeMyFriend&)' does not match any in class 'BeMyFriend'
 friend T T::makeCopy(const T&);
};

int main()
{
 Test<BeMyFriend> hmm;

 return 0;
}

这并没有真正回答你的问题,因为默认的复制构造不是朋友,但在下面的代码中:

BeMyFriend foo;
BeMyFriend bar = BeMyFriend::makeCopy(foo);

你在makeCopy里面得到了一个朋友的复制结构,下一个很可能被省略了。

无论如何,我真的无法想象一个真正的用例,只与 class 的特定构造函数而不是 whode class...

我可以通过在 friend:

之后添加 void 来让它在 GCC 中编译
friend void T::T(const T&);

然后我能够从 BeMyFriend 的构造函数访问 Test 的私有成员之一。但是,请注意这是特定于编译器的。我在 clang 中试过了,它 没有 工作。