我们在代码库中发现了令人惊讶的行为,其中友谊关系未能应用。 (目前仅与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
的所有实例化为朋友)。答案 0 :(得分:2)
别名模板与其别名类型不同义: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>>
,但我不确定如何在朋友声明中完成这项工作。