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

时间:2018-04-05 14:21:22

标签: c++ templates 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&);工作?

编辑: 这似乎与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();

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

4 个答案:

答案 0 :(得分:0)

不知怎的,我认为编译器没有很好地解析T::T(const T&),因为它不会在成员构造函数T::之前考虑名称空间T()作为对某些人的引用外部类,显然出现在错误控制台ISO C++ forbids declaration of ‘T’ with no typeprototype 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 ++标准禁止你尝试做什么。也许这是一个缺陷/疏忽;也许这是故意的。我不确定,所以我只会列出我在标准中找到的部分(除非/直到有人检查我的分析,否则可以安全地浏览部分编号)

  1. 在6.4.3.1.2中,描述了什么时候考虑命名构造函数。这涉及“inject-class-name”,它(根据12.2)表示插入类范围的类的名称。在您的上下文中,我阅读了这些部分,因为要命名构造函数,您需要T::后跟T类名。您有T::T,如果T被视为T类名,则可以使用friend。让我们看看这是如何发挥作用的。

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

  3. 在17.1.3中,声明在T中,标识符friend T::BeMyFriend typedef-name 。糟糕,

  4. 所以这有点奇怪。如果我正确地阅读了一些内容,则需要使用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尝试了一下,但没有起作用。