提问者:小点点

好友、私有函数、模板别名和decltype。。。clang拒绝这一点正确吗?


在以下代码(戈德博尔特链接):

#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;
}

Clang非常满意使用DirectResult,但会抱怨InDirectResult试图访问内部私有函数:

<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{};
            ^

我本以为访问会很好,因为模板别名是在friend类中声明的。

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

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

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


共1个答案

匿名用户

这是一只叮当作响的虫子。每[class.friend]/2:

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

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

幸运的是,该错误似乎只适用于别名声明的定义类型id;您可以通过将计算移动到帮助器类(使用嵌套的类型别名)或更简洁地移动到默认模板参数来解决此问题:

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