为啥我可以在私有类型上使用 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 &lt;&lt; typeid(f.Baz()).name() 也可以。类外的代码可以“看到”Baz() 返回的类型,如果你能掌握它,你就是无法命名它。 如果你认为这很奇怪(你可能会这样做,看到你在问它)你不是唯一的;)这个策略对于像Safe-Bool Idiom 这样的东西非常有用。 我认为要记住的是private 是为了方便以编译器可以帮助执行的方式描述 API。它并非旨在阻止Foo 的用户访问Bar 类型,因此它不会以任何方式通过返回Bar 的实例来阻止Foo 提供该访问权限。 "这段代码编译是否正确?"不,您需要#include &lt;iostream&gt;。 ;-) 如果有人正在寻找解决方法,请看我的回答(使用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 只是实际类型的别名。我叫它BarSomeDeducedType 有什么关系?我不能用它来联系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?

C语言所有变量都默认为auto,全局变量不能为auto,那全局变量默认为啥?

为啥私有字段是类型私有的,而不是实例私有的?

为啥可以从对象范围之外调用私有方法?