我们在代码库中发现了令人惊讶的行为,其中友谊关系未能应用 . (目前仅与Clang编译,版本3.6)
我们可以将它减少到这个最小的例子 . 我们假设我们有以下模板类定义:
template <int>
class Element
{};
// Forward declaration of FriendBis
template <template <int> class> class FriendBis;
class Details
{
friend class FriendBis<Element>;
int mValue = 41;
};
template <template <int> class>
class FriendBis
{
public:
void useDetails(const Details &aDetails)
{
aDetails.mValue;
}
};
这里, Details
声明 FriendBis
的实例化,其单个模板模板参数被 Element
替换为 friend
. 因此,以下客户端代码成功编译:
FriendBis<Element> fb1;
fb1.useDetails(Details());
问题
现在,让我们介绍一下额外的 trait
模板类型,其目的是将 proto
定义为 Element
模板的模板别名:
struct trait
{
template <int N>
using proto = Element<N>;
};
下面的客户端代码无法编译:
FriendBis<trait::proto> fb2;
fb2.useDetails(Details());
这对我们来说是令人惊讶的,因为 trait::proto
是 Element
的别名,但是一个编译而另一个不编译 .
-
这是预期的行为吗?
-
如果是这样,这个限制的理由是什么?
-
有解决方法吗? (同时保持有限的友谊,而不是让所有的实例化
FriendBis
成为朋友) .
1 回答
别名模板与别名的类型不同义:
trait::proto
和Element
是不同的类型 . 当template-id引用trait::proto
的特化时,它等同于替换类型 . 简单地说,trait::proto
不是Element
,但trait::proto<0>
是Element<0>
.回答你的问题:
是的,这是预期的行为
理由是别名类型可能比
Element<N>
复杂得多,它可能像Element<ElementForInt<N+1>::value>
. 然后映射是不明显的 .我无法想到一个彻头彻尾的解决方法 . 如果要检查模板模板参数是否与其他模板和别名模板帐户相同,则可以检查两个名称的实例化是否相同,例如
std::is_same<T<0>, Element<0>>
,但我不确定你如何在朋友宣言中完成这项工作 .