首页 文章

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

提问于
浏览
5

我们在代码库中发现了令人惊讶的行为,其中友谊关系未能应用 . (目前仅与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 回答

  • 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>> ,但我不确定你如何在朋友宣言中完成这项工作 .

相关问题