gcc - 使用构造函数声明会损害访问说明符并且与其他类型的成员不一致
问题描述
今天我了解到了一个令人震惊的新现实,所有流行的编译器(我可以在 godbolt.org 上使用的那些)都可以很好地使用这段代码(它可以编译),我无法解释为什么:
class A
{
protected:
A()
{ }
};
class B : private A
{
using A::A;
};
int main()
{
auto b = B{};
return 0;
}
我的推理:它应该在 失败auto b = B{};
,因为using
声明在私有范围内,因此编译器隐式提供的构造函数using
应该去那里。
无论是任何其他成员:函数还是变量,它的访问修饰符都将根据using
声明的放置位置(public
/ protected
/private
部分)来确定。
但是,现在,这不能编译:
class A
{
protected:
A(int)
{ }
};
class B : private A
{
using A::A;
};
int main()
{
auto b = B{1};
return 0;
}
这是可预测和直观的:
<source>:15:14: error: calling a protected constructor of class 'A'
auto b = B{1};
^
<source>:4:5: note: declared protected here
A(int)
但是,不幸的是,由于其他原因,它没有编译(我相信这是直观的),因为这也不是:
class A
{
protected:
A(int)
{ }
};
class B : public A
{
public:
using A::A;
};
int main()
{
auto b = B{1};
return 0;
}
似乎using
声明要么措辞不当,要么理解不力。不幸的是,许多编译器(其中一些,幸运的是,不再是在 HEAD 中)也在friend
权限问题上苦苦挣扎:
class C;
class A
{
friend class C;
protected:
A(int)
{ }
};
class B : public A
{
public:
using A::A;
};
class C
{
public:
B make_b()
{
return B{1};
}
};
int main()
{
auto b = C{}.make_b();
return 0;
}
一些语言律师可以对此进行分析并提供一些启示吗?我的假设错了吗,这应该是这样吗?
解决方案
如果类 X 没有用户声明的构造函数,则没有参数的非显式构造函数被隐式声明为默认的
class 没有用户声明的构造函数B
。B
继承自的构造A
函数不是 的构造函数B
,而是 的构造函数A
。在查找派生类的构造函数时会考虑继承的构造函数,但它们仍然不是派生类的构造函数。
该标准从未明确说明继承构造函数是否为派生类创建类似的构造函数。该标准确实表明基类的构造函数可用于查找和重载解析,就好像它们是派生类的构造函数一样。这个 IMO 意味着它们不被视为派生类的构造函数,尽管如果标准明确说明会更好。无论如何,编译器似乎是这样解释的。
编辑这是对C++14 的更改,其中继承的构造函数被注入派生类。即使在 C+14 中,这些构造函数也是隐式声明的,而不是用户声明的。
因此B
有一个public隐式声明为默认的默认构造函数,不管它继承自什么A
。
由于 using-declarator 而考虑的基类构造函数是可访问的,如果它们在用于构造基类的对象时可访问;使用声明的可访问性被忽略。
因此A::A(int)
在构造时是不可访问B
的,即使using
导入它的声明是可访问的。
推荐阅读
- ios - 存储复杂 Swift 对象的最佳方式,允许它们被提取并随应用程序一起提供
- openshift - ISTIO 允许所有出站流量到 DOMAIN
- python - Tkinter 复选框问题
- gcc - LD:使用 ALIGN() 的不同方式
- android - 适用于 Android 和 iOS 的 Facebook 应用内浏览器深度链接问题
- javascript - 在 JS 脚本中为 font-awesome 添加颜色
- python - 在 django 中导入类的正确方法是什么?
- python - django rest 框架:如何使用相关字段验证序列化程序中的规则
- pdf - 如何使用 itextstarp 在 pdf 中添加 html 复选框、单选按钮、文本框?
- unity3d - 如何在网格中对齐平铺游戏对象