次のコードでは( 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
の使用に完全に満足していますが、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{};
^
テンプレートエイリアスがフレンドクラス内で宣言されているので、アクセスは問題ないと予想していました。
ただし、私の経験では、C++標準の解釈に関しては、Clangは一般に(gccよりも)正しいです。
Clangはこのコードを拒否するのが正しいですか?そして、もしそうなら、何が欠けていますか?
注:gcc 7.x、8.x、および9.xはコードを受け入れます。
これはClangの a bug です。 [class.friend]/2 ごと:
クラスをフレンドとして宣言すると、フレンドシップを付与するクラスのプライベートおよび保護されたメンバーの名前に、フレンドクラスの基本指定子およびメンバー宣言でアクセスできることを意味します。
[class.mem] ごとに、template-declarationはmember-declaration、および [temp.pre] /2.5 ごとalias-declarationは、template-declarationのdeclarationにすることができます。したがって、メンバーエイリアステンプレートは、クラスの友達のプライベートおよび保護されたメンバーにアクセスできます。
幸いにも、バグはalias-declaration;のdefining-type-idにのみ適用されるようです。計算をヘルパークラス(ネストされたtype
エイリアスを持つ)に移動するか、より簡潔にdefault template-argumentに移動することで回避できます。
template <typename T, class U = decltype(std::declval<T>().function())>
using IndirectResult = U;