为啥我可以在私有类型上使用 auto ?
Posted
技术标签:
【中文标题】为啥我可以在私有类型上使用 auto ?【英文标题】:Why can I use auto on a private type?为什么我可以在私有类型上使用 auto ? 【发布时间】:2012-11-23 16:29:16 【问题描述】:以下代码编译并运行(vc2012 & gcc4.7.2)让我有些惊讶
class Foo
struct Bar int i; ;
public:
Bar Baz() return Bar();
;
int main()
Foo f;
// Foo::Bar b = f.Baz(); // error
auto b = f.Baz(); // ok
std::cout << b.i;
这段代码编译是否正确?为什么它是正确的?为什么我可以在私有类型上使用auto
,而我不能使用它的名称(如预期的那样)?
【问题讨论】:
观察f.Baz().i
也可以,std::cout << typeid(f.Baz()).name()
也可以。类外的代码可以“看到”Baz()
返回的类型,如果你能掌握它,你就是无法命名它。
如果你认为这很奇怪(你可能会这样做,看到你在问它)你不是唯一的;)这个策略对于像Safe-Bool Idiom 这样的东西非常有用。
我认为要记住的是private
是为了方便以编译器可以帮助执行的方式描述 API。它并非旨在阻止Foo
的用户访问Bar
类型,因此它不会以任何方式通过返回Bar
的实例来阻止Foo
提供该访问权限。
"这段代码编译是否正确?"不,您需要#include <iostream>
。 ;-)
如果有人正在寻找解决方法,请看我的回答(使用decltype
)
【参考方案1】:
auto
的规则大部分与模板类型推导相同。发布的示例的工作原理与您可以将私有类型的对象传递给模板函数的原因相同:
template <typename T>
void fun(T t)
int main()
Foo f;
fun(f.Baz()); // ok
您问,为什么我们可以将私有类型的对象传递给模板函数?因为只有类型的名称是不可访问的。该类型本身仍然可用,这就是为什么您可以将其返回给客户端代码。
【讨论】:
并且要看到name的隐私与type无关,将public: typedef Bar return_type_from_Baz;
添加到类Foo
中这个问题。现在该类型可以通过公共名称来标识,尽管它是在类的私有部分中定义的。
重复@Steve 的观点:name 的访问说明符与它的type 无关,如将private: typedef Bar return_type_from_Baz;
添加到Foo
,作为demonstrated。 typedef
'd 标识符不知道访问说明符,公共的和私有的。
这对我来说毫无意义。类型的 name 只是实际类型的别名。我叫它Bar
或SomeDeducedType
有什么关系?我不能用它来联系class Foo
的私人成员或任何东西。【参考方案2】:
访问控制应用于名称。与标准中的此示例进行比较:
class A
class B ;
public:
typedef B BB;
;
void f()
A::BB x; // OK, typedef name A::BB is public
A::B y; // access error, A::B is private
【讨论】:
【参考方案3】:chill 和 R. Martinho Fernandes 已经很好地回答了这个问题。
我就是不能放弃用哈利波特的比喻回答问题的机会:
class Wizard
private:
class LordVoldemort
void avada_kedavra()
// scary stuff
;
public:
using HeWhoMustNotBeNamed = LordVoldemort;
friend class Harry;
;
class Harry : Wizard
public:
Wizard::LordVoldemort;
;
int main()
Wizard::HeWhoMustNotBeNamed tom; // OK
// Wizard::LordVoldemort not_allowed; // Not OK
Harry::LordVoldemort im_not_scared; // OK
return 0;
https://ideone.com/I5q7gw
感谢昆汀提醒我哈利的漏洞。
【讨论】:
那里不是缺少friend class Harry;
吗?
@Quentin 你是绝对正确的!为了完整起见,可能还应该添加friend class Dumbledore;
;)
Harry 在现代 C++ 中调用 Wizard::LordVoldemort;
并没有表明他并不害怕。相反,他打电话给using Wizard::LordVoldemort;
。 (老实说,使用伏地魔并不那么自然。;-)
应该使用 Wizard::LordVoldemort吗?【参考方案4】:
为了补充其他(好的)答案,这里有一个来自 C++98 的示例,它说明问题实际上与 auto
完全无关
class Foo
struct Bar int i; ;
public:
Bar Baz() return Bar();
void Qaz(Bar)
;
int main()
Foo f;
f.Qaz(f.Baz()); // Ok
// Foo::Bar x = f.Baz();
// f.Qaz(x);
// Error: error: ‘struct Foo::Bar’ is private
不禁止使用私有类型,它只是命名类型。例如,在所有版本的 C++ 中,都可以创建该类型的未命名临时对象。
【讨论】:
【参考方案5】:对于其他来到这里并需要解决方法的人(例如,声明一个接受私有类型的函数),这就是我所做的:
void Func(decltype(Foo().Baz()) param) ...
【讨论】:
以上是关于为啥我可以在私有类型上使用 auto ?的主要内容,如果未能解决你的问题,请参考以下文章
为啥在接口列表的泛型类型中使用私有嵌套类型不是“不一致的可访问性”?
为啥 Rust 认为我的私有类型必须是公共的,除非我使用 pub(crate)?
为啥 System.DateTime 结构的布局类型为 Auto?