プライベートタイプでautoを使用できるのはなぜですか?


139

次のコードがコンパイルされて実行されることになんとなく驚いた(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名前を(期待どおり)使用できないのに、プライベートタイプで使用できるのはなぜですか?


11
そのままf.Baz().iでも問題ないことを確認しますstd::cout << typeid(f.Baz()).name()。クラスの外のコードは、Baz()それを手に入れることができ、名前を付けることができない場合に、返されるタイプを「見る」ことができます。
Steve Jessop

2
そして、それが奇妙だと思う場合(おそらくそうするでしょう、あなたがそれについて尋ねているのを見て)、あなただけではありません;)この戦略は、Safe-Boolイディオムなどの場合に非常に役立ちます。
Matthieu M.

2
覚えておかなければならないのはprivate、コンパイラーが強制できるようにAPIを説明するのに便利であることです。これBarFoo、のユーザーによるタイプへのアクセスを防ぐことを目的としていないためFoo、のインスタンスを返すことによってそのアクセスを提供することを妨げるものではありませんBar
Steve Jessop、

1
「このコードが正しくコンパイルされることは正しいですか?」いいえ#include <iostream>。;-)
LF

回答:


113

のルールautoは、ほとんどの場合、テンプレートタイプの控除と同じです。投稿された例は、プライベートタイプのオブジェクトをテンプレート関数に渡すことができるのと同じ理由で機能します。

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

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

そして、なぜ私たちはプライベート型のオブジェクトをテンプレート関数に渡すことができるのですか?タイプの名前だけにアクセスできないからです。タイプ自体はまだ使用可能です。そのため、クライアントコードにタイプを返すことができます。


32
名前のプライバシーがtypeとは関係がないことを確認するには、質問のpublic: typedef Bar return_type_from_Baz;クラスFooに追加します。クラスのプライベートセクションで定義されているにもかかわらず、タイプをパブリック名で識別できるようになりました。
Steve Jessop

1
スティーブのポイント@繰り返すには:のためのアクセス指定子の名前は、それのとは何の関係もありませんタイプを追加することで分かるように、private: typedef Bar return_type_from_Baz;するFooとして、実証しましたtypedef'd識別子は、パブリックおよびプライベートのアクセス指定子を認識しません。
damienh 2012年

これは私には意味がありません。タイプの名前は、実際のタイプのエイリアスにすぎません。私がそれを呼ぶBarSomeDeducedTypeどうかはどうですか?プライベートメンバーなどにアクセスするために使用できるようではありませんclass Foo
アインポクルム2015

107

アクセス制御は名前に適用されます。標準のこの例と比較してください:

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
}

12

この質問は、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

ハリーの抜け穴を思い出させてくれたクエンティンに感謝します。


5
friend class Harry;そこに行方不明ではないですか?
クエンティン

@クエンティンあなたは絶対に正しいです!完全をfriend class Dumbledore;
期すために

ハリーは彼がWizard::LordVoldemort;現代のC ++で呼び出すことによって怖がっていないことを示していません。代わりに、彼は電話をかけますusing Wizard::LordVoldemort;。(正直なところ、Voldemortを使用するのはそれほど自然なことではありません。;-)
LF

8

他の(良い)回答に追加するため、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 ++のすべてのバージョンで問題ありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.