C++ C++;编译器在封装行为上存在分歧——哪一个是正确的?
编译器(C++ C++;编译器在封装行为上存在分歧——哪一个是正确的?,c++,gcc,clang,private,encapsulation,C++,Gcc,Clang,Private,Encapsulation,编译器(clang-5.0.0,GCC-7.3,ICC-18和MSVC-19)在以下A成员的可访问性方面存在分歧 class A { template <class> static constexpr int f() { return 0; } template <int> struct B {}; template <class T> using C = B<f<T>()>; }; A类{ 模板静态c
clang-5.0.0
,GCC-7.3
,ICC-18
和MSVC-19
)在以下A
成员的可访问性方面存在分歧
class A {
template <class> static constexpr int f() { return 0; }
template <int> struct B {};
template <class T> using C = B<f<T>()>;
};
A类{
模板静态constexpr int f(){return 0;}
模板结构B{};
使用C=B的模板;
};
确实,考虑下面的用法:
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
}
使用D=A::C的模板;
int main(){
//|铿锵| gcc | icc | msvc
(void)A::f();//1:| f | f | f | f,(C)
(void)A::B{};//2:| B | | B | B,(C)
(void)A::C{};//3:| C,f | C | C
(void)D{};//4:| f | | C | C
}
右边的表格显示了每个编译器需要公开哪些成员才能接受代码(为C++14编译时)
IMHO、ICC和MSVC(忽略(C)
条目)看起来是正确的。除了第一行之外,GCC似乎完全忽略了可访问性
我不同意clang要求f
公开来实例化A::C
和D
。像ICC和MSVC一样,我认为只有C
需要公开。确实C
使用了f
,但这不是一个实现细节吗?请注意,C
也使用B
。如果叮当声是正确的,那么为什么不要求B
也公开呢
最后,让我们考虑<代码>(C)< /代码>条目。当MSVC第一次遇到D
的定义时,它要求C
是公开的,也就是说,MSVC抱怨C
是私有的
我的问题是:
更新:关于GCC,这似乎是评论8中报告的错误。关于
A::f()
和A::B
的问题很容易回答f
和B
是私有的,它们都没有任何其他有趣的依赖项。访问它们应该是格式错误的。gcc通常对模板中的访问控制非常宽容,在各种情况下都有一个突出的问题(我认为所有这些问题的形式都是gcc在不应该的时候允许访问,而不是在应该的时候不允许访问)
A::C
的问题更有趣。这是一个别名模板,但我们实际上是在什么上下文中查看别名的?它是在A
(在这种情况下,使C
可访问就足够了)还是在使用它的上下文中(在这种情况下,f
、B
和C
都需要可访问)。这个问题恰恰是,它仍然是活跃的:
从17.6.7[临时别名]的当前措辞来看,别名模板和访问控制之间的相互作用并不清楚。例如:
template <class T> using foo = typename T::foo;
class B {
typedef int foo;
friend struct C;
};
struct C {
foo<B> f; // Well-formed?
};
使用foo=typename T::foo的模板;
B类{
typedef int-foo;
友元结构C;
};
结构C{
foo f;//格式正确吗?
};
用B::foo
替换foo
是在befriended类C
的上下文中完成的,从而使引用格式正确,还是访问权限独立于别名模板专用化出现的上下文而确定
如果这个问题的答案是访问是独立于上下文确定的,则必须注意确保访问失败仍然被认为是“在函数类型的直接上下文中”(17.9.2[临时扣除]第8段),从而导致扣除失败,而不是硬错误
尽管问题仍然悬而未决,但方向似乎是:
CWG的共识是,别名模板的实例化(查找和访问)应该与其他模板一样,在定义上下文中,而不是在使用它们的上下文中。然而,它们仍应立即扩大
也就是说,只有C
需要公开,而f
和B
可以保持私有。这就是ICC和MSVC对其的解释。Clang有一个允许别名模板绕过access()的缺陷,这就是为什么Clang要求f
可以访问,而不是B
。但除此之外,clang似乎在使用点而不是定义点扩展别名
D
的问题应该简单地跟在A::C
后面,确切地说,CWG1554在这里没有问题。Clang是唯一一个在A::C
和D
之间有不同行为的编译器,同样是由于bug 15914
总之,
A::C
问题是一个开放的核心语言问题,但国际商会在这里实现了语言的预期含义。其他编译器都存在访问检查和模板问题 关于clang或icc和msvc在第3行是否正确的问题很有趣。此外,了解gcc的怪异也是件好事。你在这里提交了关于gcc行为的错误报告了吗?@einpoklum我还没有填写错误报告。在更多的人确认我的期望后,我会这样做。如果事实证明,叮当声也是错误的,那么我也会在那里填写一份bug报告。我不会为msvc做这件事,因为(如果我的分析正确的话)他们已经知道了。你是在/permissive-
模式下用msvc编译的吗?如果没有标志D
的话,它的行为可能会很奇怪,因为无法进行有效的专门化(但只有在没有实例化的情况下,它才是NDR)。这似乎很相关。