C++ 编译器在封装行为上存在分歧——哪一个做对了?

标签 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 时,我不同意 clang公开实例化A::C<int>D<int> .像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 ?

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

最佳答案

A::f<int>() 的问题和 A::B<0>是直截了当的回答。 fB是私有(private)的,也没有任何其他有趣的依赖项。访问它们应该是格式错误的。 gcc 通常对模板中的访问控制非常宽容,有一个 metabug在各种情况下都表现出色(我认为所有这些都是 gcc 在不应该访问时允许访问的形式,而不是在应该时不允许访问)。

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

The interaction of alias templates and access control is not clear from the current wording of 17.6.7 [temp.alias]. For example:

template <class T> using foo = typename T::foo;

class B {
  typedef int foo;
  friend struct C;
};

struct C {
  foo<B> f;    // Well-formed?
};

Is the substitution of B::foo for foo<B> done in the context of the befriended class C, making the reference well-formed, or is the access determined independently of the context in which the alias template specialization appears?

If the answer to this question is that the access is determined independently from the context, care must be taken to ensure that an access failure is still considered to be “in the immediate context of the function type” (17.9.2 [temp.deduct] paragraph 8) so that it results in a deduction failure rather than a hard error.

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

The consensus of CWG was that instantiation (lookup and access) for alias templates should be as for other templates, in the definition context rather than in the context where they are used. They should still be expanded immediately, however.

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

D<int>的问题|应该直接关注 A::C确切地说,这里的 CWG 1554 没有问题。 Clang 是唯一在 A::C 之间具有不同行为的编译器和 D ,同样是由于错误 15914。


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

关于C++ 编译器在封装行为上存在分歧——哪一个做对了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49070700/

相关文章:

Android NDK clang 编译器找不到 std::make_unique

c++ - 重写关键字在编译时抛出错误

c++ - 代码调用终止而不是抛出异常

linux - ProC 提示 Linux (CentOS 7.3) 上的 undefined symbol

c++ - Clang 只有 : A pair<path, path> 可以被放置到一个 vector 中;一对 <unique_ptr, unique_ptr> 也可以;但不是对 <path, unique_ptr> : Why?

c++ - C 或 C++ 链接器中是否有任何类型检查?

c++ - 有关模板和虚拟功能的问题

c++ - 智能指针和参数列表分配规则

gcc - 使用 Cmake 的混合语言 C++、C 和 Fortran 编译

c - for(int i = 0... 的形式是 gnu 扩展吗?