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

时间:2018-03-02 14:02:14

标签: c++ gcc clang private encapsulation

编译器(clang-5.0.0GCC-7.3ICC-18MSVC-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是私有的。

我的问题是:

  1. 在我的分析中,我是对的(ICC也是如此)?否则哪个 编译器是正确的,为什么?
  2. MSVC是否是另一个问题 two-phase instantiation bug in msvc
  3. 更新:关于GCC,这似乎是评论8 here中报告的错误。

1 个答案:

答案 0 :(得分:2)

A::f<int>()A::B<0>的问题很容易回答。 fB是私有的,并且没有任何其他有趣的依赖项。访问它们应该是不正确的。 gcc通常对模板中的访问控制非常宽容,对于各种情况都有一个metabug突出(我认为所有这些都是gcc允许访问的形式,不应该禁止访问,而不是禁止访问它什么时候应该访问)。

A::C<int>的问题更有趣。它是一个别名模板,但我们在什么情况下实际查看别名?在 A中是否(在这种情况下,使C可访问就足够了)或者是否在其使用的上下文中(在这种情况下,所有fBC都需要访问权限。这个问题恰好是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::foofoo<B>的替换是在友好类C的上下文中完成,使参考格式正确,还是独立于上下文确定的访问权限别名模板专业化出现了吗?

     

如果这个问题的答案是独立于上下文确定访问权限,则必须注意确保访问失败仍被认为是“在函数类型的直接上下文中”(17.9.2 [ temp.deduct]第8段)因此导致演绎失败而不是硬错误。

虽然问题仍未解决,但方向似乎是:

  

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

也就是说,只有C需要公开,fB可以保密。这就是ICC和MSVC如何解释它。 Clang有一个错误,允许别名模板绕过访问(15914),这就是clang要求f可访问而不是B的原因。但除此之外,clang似乎在使用点而不是定义点扩展了别名。

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

总而言之,A::C的问题是一个开放的核心语言问题,但ICC在此实现了该语言的预期含义。其他编译器都存在访问检查和模板的问题。