C++编译器在封装行为上有所不同 - 哪一个正确?

Cas*_*eri 38 c++ gcc encapsulation private clang

编译器(clang-5.0.0,GCC-7.3,ICC-18MSVC-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一样,我认为CC需要公开.这是事实,C用途f,但它是不实现的细节?请注意,C也使用B.如果铿锵是正确的,为什么它也不需要B公开?

最后,让我们考虑一下(C)条目.Msvc要求C在第一次遇到定义时公开D,即MSVC抱怨C私有.

我的问题是:

  1. 在我的分析中,我是对的吗(ICC也是如此)?否则哪个编译器是正确的,为什么?
  2. MSVC是msvc中另一个 两阶段实例化错误的问题吗?

更新:关于GCC,这似乎是在评论8已报告的错误,在这里.

Bar*_*rry 3

A::f<int>()和的问题A::B<0>很容易回答。f并且B都是私有的,并且没有任何其他有趣的依赖关系。访问它们应该是格式错误的。gcc 通常对模板中的访问控制非常宽松,有一个元错误对于各种情况都有一个突出的错误(我认为所有这些都是 gcc 在不应该访问时允许访问的形式,而不是在应该时不允许访问的形式) 。

\n\n

的问题A::C<int>就更有趣了。这是一个别名模板,但我们实际上在什么上下文中查看别名?它是在其中 A(在这种情况下,使其C可访问就足够了)还是在使用它的上下文中(在这种情况下,fBC都需要可访问)。这个问题正是CWG 1554,该问题仍然有效:

\n\n
\n

从 17.6.7 [temp.alias] 的当前措辞来看,别名模板和访问控制的交互尚不清楚。例如:

\n\n
template <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
Run Code Online (Sandbox Code Playgroud)\n\n

的替换是在友好类的上下文中完成的B::foofoo<B>C,从而使引用格式良好,还是独立于别名模板专业化出现的上下文确定访问权限?

\n\n

如果这个问题的答案是访问是独立于上下文确定的,则必须注意确保访问失败仍然被认为是 \xe2\x80\x9cin 函数类型 \xe2\x80\ 的直接上下文x9d(17.9.2 [temp.deduct] 第 8 段),以便它导致推导失败而不是硬错误。

\n
\n\n

尽管问题仍然悬而未决,但方向似乎是:

\n\n
\n

CWG 的共识是,别名模板的实例化(查找和访问)应该与其他模板一样,在定义上下文中而不是在使用它们的上下文中。然而,它们仍应立即扩大。

\n
\n\n

也就是说,只需C公开即可,并且f可以B保持私密。这就是 ICC 和 MSVC 的解释方式。Clang 有一个允许别名模板规避访问的 bug ( 15914 ),这就是为什么 clang 需要f可访问而不是可访问B。但除此之外,clang 似乎是在使用时而不是定义时扩展别名。

\n\n

问题D<int>应该完全遵循A::C,这里 CWG 1554 没有问题。A::CClang 是唯一一个在和之间具有不同行为的编译器D,同样是由于错误 15914。

\n\n
\n\n

总而言之,问题是A::C是一个开放的核心语言问题,但ICC在这里实现了语言的本意。其他编译器都存在访问检查和模板问题。

\n