Cas*_*eri 38 c++ gcc encapsulation private clang
编译器(clang-5.0.0,GCC-7.3,ICC-18和MSVC-19)发散的成员的可访问性WRT A下面.
class A {
template <class> static constexpr int f() { return 0; }
template <int> struct B {};
template <class T> using C = B<f<T>()>;
};
Run Code Online (Sandbox Code Playgroud)
确实,请考虑以下用法:
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
}
Run Code Online (Sandbox Code Playgroud)
右边的表格显示了每个编译器需要公开接受代码的成员(当编译为C++ 14时).
恕我直言,ICC和MSVC(忽略(C)条目)看起来正确.除第一行外,GCC似乎完全忽略了可访问性.
我不同意铿锵时,它需要f是公共实例化 A::C<int>和D<int>.像ICC和MSVC一样,我认为C只C需要公开.这是事实,C用途f,但它是不实现的细节?请注意,C也使用B.如果铿锵是正确的,为什么它也不需要B公开?
最后,让我们考虑一下(C)条目.Msvc要求C在第一次遇到定义时公开D,即MSVC抱怨C私有.
更新:关于GCC,这似乎是在评论8已报告的错误,在这里.
A::f<int>()和的问题A::B<0>很容易回答。f并且B都是私有的,并且没有任何其他有趣的依赖关系。访问它们应该是格式错误的。gcc 通常对模板中的访问控制非常宽松,有一个元错误对于各种情况都有一个突出的错误(我认为所有这些都是 gcc 在不应该访问时允许访问的形式,而不是在应该时不允许访问的形式) 。
的问题A::C<int>就更有趣了。这是一个别名模板,但我们实际上在什么上下文中查看别名?它是在其中 A(在这种情况下,使其C可访问就足够了)还是在使用它的上下文中(在这种情况下,f、B和C都需要可访问)。这个问题正是CWG 1554,该问题仍然有效:
\n\n\n从 17.6.7 [temp.alias] 的当前措辞来看,别名模板和访问控制的交互尚不清楚。例如:
\n\nRun Code Online (Sandbox Code Playgroud)\n\ntemplate <class T> using foo = typename T::foo;\n\nclass B {\n typedef int foo;\n friend struct C;\n};\n\nstruct C {\n foo<B> f; // Well-formed?\n};\n的替换是在友好类的上下文中完成的
\n\nB::foo吗foo<B>C,从而使引用格式良好,还是独立于别名模板专业化出现的上下文确定访问权限?如果这个问题的答案是访问是独立于上下文确定的,则必须注意确保访问失败仍然被认为是 \xe2\x80\x9cin 函数类型 \xe2\x80\ 的直接上下文x9d(17.9.2 [temp.deduct] 第 8 段),以便它导致推导失败而不是硬错误。
\n
尽管问题仍然悬而未决,但方向似乎是:
\n\n\n\n\nCWG 的共识是,别名模板的实例化(查找和访问)应该与其他模板一样,在定义上下文中而不是在使用它们的上下文中。然而,它们仍应立即扩大。
\n
也就是说,只需C公开即可,并且f可以B保持私密。这就是 ICC 和 MSVC 的解释方式。Clang 有一个允许别名模板规避访问的 bug ( 15914 ),这就是为什么 clang 需要f可访问而不是可访问B。但除此之外,clang 似乎是在使用时而不是定义时扩展别名。
问题D<int>应该完全遵循A::C,这里 CWG 1554 没有问题。A::CClang 是唯一一个在和之间具有不同行为的编译器D,同样是由于错误 15914。
总而言之,问题是A::C是一个开放的核心语言问题,但ICC在这里实现了语言的本意。其他编译器都存在访问检查和模板问题。