【问题标题】:C++ compilers diverge in encapsulation behavior - which one gets it right?C++ 编译器在封装行为上存在分歧——哪一个做对了?
【发布时间】:2018-08-10 18:16:18
【问题描述】:

编译器(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&lt;int&gt;D&lt;int&gt; 时,我不同意 clang。像 ICC 和 MSVC 一样,我认为 C 并且只有 C 需要公开。 C 确实使用了f,但这不是实现细节吗?注意C 也使用B。如果 clang 是正确的,那为什么它不需要 B 也是公开的呢?

最后,让我们考虑(C) 条目。 MSVC在第一次遇到D的定义时要求C是public的,即MSVC抱怨C是private的。

我的问题是:

  1. 我的分析是否正确(ICC 也是如此)?否则哪个其他编译器是正确的,为什么?
  2. 是 MSVC 问题的另一个体现 two-phase instantiation bug in msvc?

更新:关于 GCC,这似乎是评论 8 here 中报告的错误。

【问题讨论】:

  • clang 或 icc 和 msvc 在第 3 行是否正确的问题很有趣。此外,很高兴知道 gcc 的怪异之处。您是否在这里提交了有关 gcc 行为的错误报告?
  • @einpoklum 我还没有填写错误报告。在更多的人确认我的期望后,我会这样做。如果事实证明 clang 也是错误的,那么我也会在那里填写错误报告。我不会为 msvc 做,因为(如果我的分析是正确的)他们已经知道了。
  • 您是否在/permissive- 模式下使用MSVC 进行编译?如果没有那个标志,它可能会表现得很奇怪
  • D 格式不正确,因为无法进行有效的特化(但只有在未实例化时才为 NDR)。
  • CWG1554 似乎相关。

标签: c++ gcc clang private encapsulation


【解决方案1】:

A::f&lt;int&gt;()A::B&lt;0&gt; 的问题很容易回答。 fB 是私有的,并且没有任何其他有趣的依赖项。访问它们应该是格式错误的。 gcc 通常对模板中的访问控制非常宽容,在各种情况下都有一个 metabug )。

A::C&lt;int&gt;的问题比较有趣。这是一个别名模板,但我们实际上在什么上下文中查看别名?它是A(在这种情况下,使C 可访问就足够了)还是在使用它的上下文中(在这种情况下,fB、和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&lt;B&gt; 是在友好类 C 的上下文中完成的,使引用格式正确,还是独立于别名模板特化出现的上下文确定访问?

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

虽然问题依然悬而未决,但方向似乎是:

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

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

D&lt;int&gt; 的问题应该完全按照A::C 进行,这里的 CWG 1554 没有问题。 Clang 是唯一一个在 A::CD 之间具有不同行为的编译器,同样是由于错误 15914。


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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-29
    • 2010-10-30
    • 2010-12-14
    • 1970-01-01
    • 1970-01-01
    • 2013-03-03
    • 1970-01-01
    • 2022-08-03
    相关资源
    最近更新 更多