为什么在友谊方面,别名模板的处理方式与别名类型模板不同?

时间:2015-11-02 15:05:59

标签: c++ templates c++11 friend

我们在代码库中发现了令人惊讶的行为,其中友谊关系未能应用。 (目前仅与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::protoElement的别名,但是一个编译而另一个不编译。

  • 这是预期的行为吗?
    • 如果是这样,这个限制的理由是什么?
    • 有解决方法吗? (同时保持有限的友谊,而不是将FriendBis的所有实例化为朋友)。

1 个答案:

答案 0 :(得分:2)

别名模板与其别名类型不同义:trait::protoElement是不同的类型。当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>>,但我不确定如何在朋友声明中完成这项工作。