如何使模板参数的构造函数成为友元?
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 type
和 prototype 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 有人正在检查我的分析) :
在6.4.3.1.2中,有关于何时考虑命名构造函数的描述。这涉及 "injected-class-name" ,它(根据 12.2)表示插入 class 范围的 class 的名称。在你的上下文中,我读到这些部分是说要命名构造函数,你需要 T::
后跟 class-name of T
。你有 T::T
,如果 T
被认为是 T
的 class-name,它就可以工作。让我们看看结果如何。
在 15.1.1 中,声明您的 friend
声明需要命名一个构造函数。它接着说 class-name 不应是 typedef-name。所以我们应该没问题,只要 T
不是 typedef-name.
在 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 中试过了,它 没有 工作。
在 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 type
和 prototype 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 有人正在检查我的分析) :
在6.4.3.1.2中,有关于何时考虑命名构造函数的描述。这涉及 "injected-class-name" ,它(根据 12.2)表示插入 class 范围的 class 的名称。在你的上下文中,我读到这些部分是说要命名构造函数,你需要
T::
后跟 class-name ofT
。你有T::T
,如果T
被认为是T
的 class-name,它就可以工作。让我们看看结果如何。在 15.1.1 中,声明您的
friend
声明需要命名一个构造函数。它接着说 class-name 不应是 typedef-name。所以我们应该没问题,只要T
不是 typedef-name.在 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 中试过了,它 没有 工作。