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

Ad *_*d N 5 c++ templates friend c++11

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

在这里,Details声明FriendBis用其单个模板模板参数替换的实例化Element是它的friend.因此,以下客户端代码成功编译:

FriendBis<Element> fb1;
fb1.useDetails(Details());
Run Code Online (Sandbox Code Playgroud)

 问题

现在,让我们介绍一个额外的trait模板化类型,其目的是定义proto模板的模板别名Element:

struct trait
{
    template <int N>
    using proto = Element<N>;
};
Run Code Online (Sandbox Code Playgroud)

下面的客户端代码无法编译:

FriendBis<trait::proto> fb2;
fb2.useDetails(Details());
Run Code Online (Sandbox Code Playgroud)

这对我们来说是令人惊讶的,因为trait::proto它是别名Element,但是一个编译而另一个不编译.

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

Tar*_*ama 2

别名模板与其别名的类型不同义:trait::proto并且Element是不同的类型。当 template-id 引用 的特化时,trait::proto 相当于替换类型。简单地说,trait::proto不是Elementtrait::proto<0> 而是 Element<0>

回答您的问题:

  • 是的,这是预期的行为

  • 基本原理是别名类型可能比简单的复杂得多Element<N>,它可能类似于Element<ElementForInt<N+1>::value>. 那么映射是不明显的。

  • 我无法立即想到解决方法。如果您想检查模板模板参数是否与其他模板相同考虑别名模板,您可以检查两个名称的实例化是否具有相同的类型,例如std::is_same<T<0>, Element<0>>,但我不确定如何使其工作在朋友宣言中。

  • 这是 [CWG 1286](http://wg21.link/CWG1286)。另外,“trait::proto”和“Element”不是类型。 (2认同)