How to befriend one instantiation of template class with other one of the same template
我有一个模板类,该模板类仅将模板参数用于用户界面功能,并在示例中将数据内部保存在某些字段中,例如i。 i的类型未模板化。所有实例都必须协同工作并可以访问i,但不能从模板用户外部访问i。
问题在于如何使朋友成为同一模板的不同实例。他们必须访问其他实例化的私有数据,并且在内部所有这些实例化都是同一回事。
假设我想像下面的示例一样比较模板的不同实例。这里用于与其他模板参数实例化的i是私有的,如果我尝试声明此模板friend,它将无法正常工作。
我知道,我可以在类模板之外进行operator ==的定义,但是假设我需要执行某些功能,例如is_same,该函数必须是成员,并且我希望它的主体位于类声明的内部。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include <iostream>
template<typename T>
class C
{
template<typename U> // ????????
friend class C;
template<typename U>
friend bool operator == (C const& c1, C<U> const& c2) noexcept
{
return c1.i == c2.i;
}
int i;
public:
C(): i {0} {}
C(int val): i {val} {}
template<typename U>
bool is_same(C<U> const& c)
{
return c.i == i;
}
}; // template class C
int main()
{
C<int> c1 {5};
C<char> c2 {5};
if (c1 == c2) std::cout <<"All ok" << std::endl;
else std::cout <<"Very bad" << std::endl;
if (c1.is_same(c2)) std::cout <<"All ok" << std::endl;
else std::cout <<"Very bad" << std::endl;
return 0;
} |
编译错误(gcc 5.1 -std = c ++ 14)
1
| ~/main.cpp:15:9: error: ?int C<char>::i? is private |
-
您最近有没有问过同样的问题?
-
干得好,您发现了Clang错误AFAICS。
-
毕竟,您似乎在询问XY问题。您能否详细说明需要此的实际用例。
-
友谊不是传递的。您的op ==是C 的朋友,但是您的op ==访问私有的C :: i。对于op ==,C 是C 的朋友并不重要。
-
@π?ντα?ε?我需要它得到真正的回答,而不仅仅是垃圾邮件和减少内容。
-
我没有拒绝您的问题,但是确实如此,这似乎是一个XY问题。我只是要求您使问题更清楚,并详细说明您实际需要这些东西的实际用例。
-
@Johannes Schaub它不是可传递的,但对于两个朋友都将被实例化,并且必须一切正常。有用。您可以与我公开查看。当您在类声明中声明friends成员body时,您不能在两个与当前实例不同的实例中进行操作。就像我上面所做的那样,必须是最新的。如果您不信任我,请尝试使用真实代码。
-
为什么需要朋友?您可以将operator ==定义为成员函数,并且此代码可以正常编译。
-
@ user1034749如果没有朋友定义,它将完全无法编译。如果我可以编译它,我不问
-
如果您不能信任理解情况的人并评论您的代码,那么作为stackoverflow主请求者的承运人就不会太成功。
-
我移动template bool运算符==(C const&c2)const noexcept {return i == c2.i; }到public部分,并使用gcc 5.2 / clang 3.7进行所有编译
-
@π?ντα?ε?如我所说,模板参数是执行用户界面所必需的。它是对特质类的引用。该代码必须可以工作,因为C根本不是模板。一些成员函数使用的模板参数来知道如何进行参数转换
-
@Johannes Schaub我看到了你的答案,但他们没有帮助我
-
@ user1034749不,它不会在公共部分与运算符一起编译。它不是私有部分中的运算符,它是一个朋友函数,它仅在类内部具有定义
-
如果不适合您,则此代码可以编译好pastebin.com/m1vFuPDW,可能是gcc 5.1包含5.2中已修复的错误,只需更新编译器
-
就像我说的@ user1034749一样,我甚至可以将运算符==声明为外部函数,但是对于其他成员来说这是不可能的,并且不能解决所有问题。
-
@ user1034749 GCC 5.1也会对此进行编译:melpon.org/wandbox/permlink/g8bcjrncGzqn7EnO
-
@ user1034749是的,它可以工作。它不是两个操作数的运算符,但是我可以在模板之外声明两个操作数的任何运算符。它适用于其他功能。谢谢
-
但是您可以举一个必须是朋友而不是成员函数的运算符的例子吗?
-
@ user1034749我可以在正文中声明诸如:template friend bool operator == () (C const&, C const&)和类定义template inline bool operator == ()...之外的内容,依此类推,它将与私有成员一起工作。或者我可以在类声明中定义all,但是在这种情况下,一个操作数,第一个或第二个必须只是C,而不是模板。在您的情况下,运算符不是朋友函数,而是一个成员,右边==的值必须为* this
-
@ user1034749对不起,==的左边必须是* this
-
@Johannes Schaub可能是您正确的选择,但我不明白,如果在班级之外定义了运算符的主体,为什么声明朋友运算符有效,而在正文中则不起作用。我使用该sintaxis所做的-只是为了避免在类之外编写内联运算符。而且我在很多情况下都这样做,通常可以正常工作
-
@ user1034749这是具有类定义gist.github.com/romikforest/e5994ec20a0eb25e0399的版本
-
@ user1034749这就是otisde类gist.github.com/romikforest/5554db6596319867026c我一直在想它们是等效的
那么为什么不使用类内部进行编译,
并在课外进行编译。
当你有这个:
1 2 3 4 5 6 7 8
| template <typename T> struct C {
template<typename U>
friend bool operator == (C const& c1, C<U> const& c2)
{
return c1.i == c2.i;
}
};
C<int>() == C<char>() |
在class C的外部范围内生成了两个::operator==,在我们的示例中是全局名称空间:::operator==(C const& c1, C const& c2)(1)和
::operator==(C const& c1, C const& c2)(2),第一个是C的朋友,第二个是C的朋友。 并且编译器使用(1)。 (1)是C的唯一朋友,因此编译时错误。
但是,如果您编写这样的代码:
1 2 3 4 5 6 7 8 9 10 11 12
| template <typename T> struct C {
template<typename U1, typename U>
friend bool operator == (C<U1> const& c1, C<U> const& c2);
private:
int i;
};
template<typename U1, typename U>
bool operator == (C<U1> const& c1, C<U> const& c2)
{
return c1.i == c2.i;
} |
其中operator==不在C内部实现,则编译器不会在其已使用的类C范围之外生成operator==,
因此我们有::operator==和C的哪个朋友。 最后,当您拥有:
1 2 3 4 5 6 7 8
| template <typename T> struct C {
template<typename U1, typename U>
friend bool operator == (C<U1> const& c1, C<U> const& c2) {
return c1.i == c2.i;
}
private:
int i;
}; |
如果是C() == C(),则有两个由::operator== ::operator==(Cconst&, Cconst&)的编译器变体生成,并且
::operator==(Cconst&, Cconst&)和编译器应在此处生成编译时错误。
参考c ++ 11标准最新草案,第14.5.4和11.3.5节。