在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&);
工作?
编辑: 这似乎与Make Friend the constructor of a template class中解决的问题不同。那里的问题是在声明中处理循环模板参数。它不处理实际模板参数的构造函数。
Foo
的类型是普通的模板化类,而不是我的示例中的T
之类的模板参数。类似于friend Foo<B>::Foo<B>()
的提交应该编译得很好,不像我在friend T::T(const T&)
这里遇到的问题。
编辑: 如果这最终有问题,我正在用gcc 7.2进行编译。
编辑:
我还想澄清一下,C ++确实支持让构造函数成为朋友。例如,http://en.cppreference.com/w/cpp/language/friend中第一个示例中的friend X::X(char), X::~X();
。
这里的问题是如何使模板参数的构造函数成为朋友。
答案 0 :(得分:0)
不知怎的,我认为编译器没有很好地解析T::T(const T&)
,因为它不会在成员构造函数T::
之前考虑名称空间T()
作为对某些人的引用外部类,显然出现在错误控制台ISO C++ forbids declaration of ‘T’ with no type
和prototype for ‘BeMyFriend::BeMyFriend(const BeMyFriend&)’ does not match any in class ‘BeMyFriend’
中,其中编译器公然试图获取从类外部导出的任何定义或声明,因此用户应强制T::
引入引用友好类的编译器,如T&::
,这足以消除歧义。
您可以检查here实例化器&#34;是否正常工作&#34;完美而且价值得到了很好的结合。
如果,无论如何,您在此example中看到显示的错误std::__cxx11::string Test<BeMyFriend>::mything’ is private within this context areyoumyfriend.mything;
描述了对私有值的违规访问状态,这是因为成员函数不能简单地与主机类成为友好关系。
答案 1 :(得分:0)
我认为C ++标准禁止你尝试做什么。也许这是一个缺陷/疏忽;也许这是故意的。我不确定,所以我只会列出我在标准中找到的部分(除非/直到有人检查我的分析,否则可以安全地浏览部分编号):
在6.4.3.1.2中,描述了什么时候考虑命名构造函数。这涉及“inject-class-name”,它(根据12.2)表示插入类范围的类的名称。在您的上下文中,我阅读了这些部分,因为要命名构造函数,您需要T::
后跟T
的类名。您有T::T
,如果T
被视为T
的类名,则可以使用friend
。让我们看看这是如何发挥作用的。
在15.1.1中,声明您的T
声明需要命名构造函数。接着说 class-name 不应该是 typedef-name 。所以只要class Test
不是 typedef-name ,我们就应该没问题。
在17.1.3中,声明在T
中,标识符friend T::BeMyFriend
是 typedef-name 。糟糕,
所以这有点奇怪。如果我正确地阅读了一些内容,则需要使用T
来命名构造函数,当然这只适用于这个特定的示例。对于其他模板参数,这将查找名为“BeMyFriend”的成员函数。不是你想要的。
(您可能会注意到,在声明为friend的构造函数的示例中,使用的类名始终是定义类时使用的名称。在这种情况下,没有typedef正在进行,所以这个问题不会出现。)
解决方案?我认为你需要让班级T
成为朋友,在{{1}}朋友中创建一个静态函数并从构造函数中调用它,或者找到一个(更好?)如何在不使用朋友的情况下做你想做的事。当我看到一个模板参数成为朋友时,脑海里浮现着警告旗帜 - 这是合法的,但往往违背了封装原则。
答案 2 :(得分:0)
构造函数是非常特殊的方法。他们没有返回类型(甚至无效),也没有真实姓名。不能在朋友声明中使用的AFAIK。
可能的解决方法是在类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
内得到了一个朋友副本建筑,下一个可能会被删除。
无论如何,我无法真正想象一个真正的用例,只能从一个类而不是whode类中只有一个特定的构造函数...
答案 3 :(得分:0)
通过在void
之后添加friend
,我可以在GCC中对其进行编译:
friend void T::T(const T&);
然后,我可以从Test
的构造函数访问BeMyFriend
的一个私有成员。但是,请注意,这是编译器特定的。我用clang尝试了一下,但没有起作用。