编译器(clang-5.0.0
,GCC-7.3
,ICC-18
和MSVC-19
)与w.r.t分道扬..以下A
成员的可访问性。
class A {
template <class> static constexpr int f() { return 0; }
template <int> struct B {};
template <class T> using C = B<f<T>()>;
};
确实,请考虑以下用法:
template <class T> using D = A::C<T>;
int main() {
// | clang | gcc | icc | msvc
(void) A::f<int>(); // 1: | f | f | f | f, (C)
(void) A::B<0>{}; // 2: | B | | B | B, (C)
(void) A::C<int>{}; // 3: | C, f | | C | C
(void) D<int>{}; // 4: | f | | C | C
}
右边的表格显示了每个编译器需要公开接受代码的成员(当编译为C ++ 14时)。
恕我直言,ICC和MSVC(忽略(C)
条目)看起来是正确的。除第一行外,GCC似乎完全忽略了可访问性。
当需要f
公开实例化A::C<int>
和D<int>
时,我不同意clang。与ICC和MSVC一样,我认为C
并且只有C
需要公开。 C
确实使用f
,但它不是实现细节吗?请注意,C
也使用B
。如果clang是正确的,那么为什么它也不需要B
公开?
最后,让我们考虑一下(C)
条目。 Msvc要求C
在首次遇到D
的定义时公开,即MSVC抱怨C
是私有的。
更新:关于GCC,这似乎是评论8 here中报告的错误。
答案 0 :(得分:2)
A::f<int>()
和A::B<0>
的问题很容易回答。 f
和B
是私有的,并且没有任何其他有趣的依赖项。访问它们应该是不正确的。 gcc通常对模板中的访问控制非常宽容,对于各种情况都有一个metabug突出(我认为所有这些都是gcc允许访问的形式,不应该禁止访问,而不是禁止访问它什么时候应该访问)。
A::C<int>
的问题更有趣。它是一个别名模板,但我们在什么情况下实际查看别名?在 A
中是否(在这种情况下,使C
可访问就足够了)或者是否在其使用的上下文中(在这种情况下,所有f
,B
和C
都需要访问权限。这个问题恰好是CWG 1554,仍然有效:
别名模板和访问控制的交互在17.6.7 [temp.alias]的当前措辞中并不清楚。例如:
template <class T> using foo = typename T::foo; class B { typedef int foo; friend struct C; }; struct C { foo<B> f; // Well-formed? };
B::foo
对foo<B>
的替换是在友好类C
的上下文中完成,使参考格式正确,还是独立于上下文确定的访问权限别名模板专业化出现了吗?如果这个问题的答案是独立于上下文确定访问权限,则必须注意确保访问失败仍被认为是“在函数类型的直接上下文中”(17.9.2 [ temp.deduct]第8段)因此导致演绎失败而不是硬错误。
虽然问题仍未解决,但方向似乎是:
CWG的共识是,别名模板的实例化(查找和访问)应该与定义上下文中的其他模板一样,而不是在使用它们的上下文中。但是,它们仍应立即扩展。
也就是说,只有C
需要公开,f
和B
可以保密。这就是ICC和MSVC如何解释它。 Clang有一个错误,允许别名模板绕过访问(15914),这就是clang要求f
可访问而不是B
的原因。但除此之外,clang似乎在使用点而不是定义点扩展了别名。
D<int>
的问题应该完全遵循A::C
,这里没有CWG 1554的问题。 Clang是A::C
和D
之间唯一具有不同行为的编译器,同样是由于错误15914。
总而言之,A::C
的问题是一个开放的核心语言问题,但ICC在此实现了该语言的预期含义。其他编译器都存在访问检查和模板的问题。