web-dev-qa-db-ja.com

フレンド、プライベート関数、テンプレートエイリアス、およびdecltype ...これを拒否するclangは正しいですか?

次のコードでは( godbolt link ):

#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の使用に完全に満足していますが、IndirectResultprivateInner関数にアクセスしようとしていることを非難します。

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

テンプレートエイリアスがフレンドクラス内で宣言されているので、アクセスは問題ないと予想していました。

ただし、私の経験では、C++標準の解釈に関しては、Clangは一般に(gccよりも)正しいです。

Clangはこのコードを拒否するのが正しいですか?そして、もしそうなら、何が欠けていますか?

注:gcc 7.x、8.x、および9.xはコードを受け入れます。

6
Matthieu M.

これはClangの a bug です。 [class.friend]/2 ごと:

クラスをフレンドとして宣言すると、フレンドシップを付与するクラスのプライベートおよび保護されたメンバーの名前に、フレンドクラスの基本指定子およびメンバー宣言でアクセスできることを意味します。

[class.mem] ごとに、template-declarationmember-declaration、および [temp.pre] /2.5 ごとalias-declarationは、template-declarationdeclarationにすることができます。したがって、メンバーエイリアステンプレートは、クラスの友達のプライベートおよび保護されたメンバーにアクセスできます。

幸いにも、バグはalias-declaration;のdefining-type-idにのみ適用されるようです。計算をヘルパークラス(ネストされたtypeエイリアスを持つ)に移動するか、より簡潔にdefault template-argumentに移動することで回避できます。

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