朋友、私有函数、模板别名和 decltype ……clang 拒绝这个是否正确?

Mat*_* M. 6 c++ friend language-lawyer

在以下代码中(godbolt 链接):

#include <utility>

struct Friend {
    class Inner {
        friend struct Friend;

        int function() { return 0; }
    };

    using DirectResult = decltype(std::declval<Inner>().function());

    template <typename T>
    using IndirectResult = decltype(std::declval<T>().function());
};

int main() {
    Friend::DirectResult direct{};
    Friend::IndirectResult<Friend::Inner> indirect{};
    return direct + indirect;
}
Run Code Online (Sandbox Code Playgroud)

Clang 对 的使用非常满意DirectResult,但会抱怨IndirectResult试图访问 的private函数Inner

<source>:13:55: error: 'function' is a private member of 'Friend::Inner'    
    using IndirectResult = decltype(std::declval<T>().function());
                                                      ^
<source>:18:13: note: in instantiation of template type alias 'IndirectResult' requested here
    Friend::IndirectResult<Friend::Inner> indirect{};
            ^
Run Code Online (Sandbox Code Playgroud)

我本来希望访问没问题,因为模板别名是在朋友类中声明的。

但是,根据我的经验,Clang 在解释 C++ 标准时通常是正确的(比 gcc 更正确)。

Clang 拒绝此代码是否正确?如果是这样,我错过了什么?

注意:gcc 7.x、8.x 和 9.x 接受代码。

eca*_*mur 4

这是Clang 中的一个错误。每[class.friend]/2

将类声明为友元意味着可以在友元类的基说明符和成员声明中访问授予友谊的类中的私有和受保护成员的名称。

根据[class.mem]模板声明可以是成员声明,​​根据[temp.pre]/2.5别名声明可以是模板声明中的声明。因此,成员别名模板可以访问该类友元的私有成员和受保护成员。

幸运的是,该错误似乎仅适用于别名声明定义类型 ID;您可以通过将计算移至辅助类(带有嵌套别名)或更简洁地移至默认模板参数来解决此问题:type

template <typename T, class U = decltype(std::declval<T>().function())>
using IndirectResult = U;
Run Code Online (Sandbox Code Playgroud)