2012-11-23 8 views
118

は、私は、次のコードはコンパイルして実行(vc2012 & gcc4.7.2)プライベートタイプでautoを使用できるのはなぜですか?

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を使用するのはなぜですか(名前は使用できません)。

+10

'のstdがあるとして' f.Baz()i'は、もOKであることを確認::。 cout << typeid(f.Baz()).name() '。クラスの外のコードは 'Baz()'によって返される型を "見る"ことができます。もしそれを保持できれば、名前を付けることはできません。 –

+2

もしあなたがそれが奇妙だと思うなら(それはあなたがそれについて尋ねているのを見ているだけではありません)、この戦略は[Safe-Bool Idiom](http:// www .artima.com/cppsource/safebool.html)でも。 –

+2

私は覚えておくべきことは、コンパイラが実行するのを助けることができるようにAPIを記述するための便宜として、 'private'がそこにあるということです。 'Foo'のユーザが' Bar'型へのアクセスを防ぐことを意図していないので、 'Bar'のインスタンスを返すことによってそのアクセスを提供することから何らかの形でFooを妨げることはありません。 –

答えて

100

autoのルールは、ほとんどの場合、テンプレートタイプの控除のルールと同じです。例では、テンプレート関数にプライベートなタイプのオブジェクトを渡すことができ、同じ理由で作品を掲載:

template <typename T> 
void fun(T t) {} 

int main() { 
    Foo f; 
    fun(f.Baz());   // ok 
} 

そして、なぜ私たちは、あなたが聞いて、テンプレート関数にプライベートなタイプのオブジェクトを渡すことができますか?タイプの名前にのみアクセスできないためです。型自体は引き続き使用できます。なぜそれをクライアントコードにまったく返すことができるのでしょうか。

+25

そして、* name *のプライバシーが* type *と何の関係もないことを知るためには、 'public:typedef Bar return_type_from_Baz;'を質問のクラス 'Foo'に追加してください。クラスのプライベートセクションで定義されているにもかかわらず、パブリック名で型を識別できるようになりました。 –

+1

@ Steveのポイントを繰り返す:_name_のアクセス指定子は、「private:typedef Bar return_type_from_Baz;」を「Foo」に追加することで見られるように_type_とは関係ありません(http://ideone.com/iKv2bQ)。 'typedef'd識別子は、アクセス指定子(publicとprivate)に気付かれません。 – damienh

+0

これは私には意味がありません。型の_name_は実際の型のエイリアスにすぎません。私が 'Bar'または' SomeDeducedType'と呼ぶとどうなるでしょうか?私は 'class Foo'や何かのプライベートメンバーにアクセスするのに使うことはできません。 – einpoklum

98

にアクセス制御が適用されます。標準からこの例と比較:

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 
} 
5

は、他の(良い)の回答に追加するには、ここでの問題は、本当にすべての

autoとしなければならないことを示してC++ 98からの例です
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++で、そのタイプの名前のないテンポラリを作成するのは問題ありません。

8

この質問は、チルとR・マルティニョ・フェルナンデスの両方から非常によく答えられています。

私はハリーポッターのアナロジーで質問に答えるために、この機会を渡すことができませんでした:

class Wizard 
{ 
private: 
    class LordVoldemort 
    { 
     void avada_kedavra() 
     { 
      // scary stuff 
     } 
    }; 
public: 
    using HeWhoMustNotBeNamed = LordVoldemort; 
}; 

int main() 
{ 
    Wizard::HeWhoMustNotBeNamed tom; // OK 
    Wizard::LordVoldemort not_allowed; // Not OK 
    return 0; 
} 
+4

そこには「友人クラスハリー」はありませんか? – Quentin

関連する問題