เหตุใดฉันจึงใช้รถยนต์เป็นประเภทส่วนตัว


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
และถ้าคุณคิดว่ามันแปลก ๆ (ซึ่งคุณอาจจะทำเห็นเป็นคุณจะถามเกี่ยวกับเรื่องนี้) คุณไม่ได้เป็นเพียงหนึ่ง;) กลยุทธ์นี้เป็นอันยิ่งใหญ่ที่มีประโยชน์สำหรับสิ่งที่ต้องการตู้เซฟ Bool สำนวนว่า
Matthieu M.

2
ฉันคิดว่าสิ่งที่ต้องจำคือprivateมีความสะดวกในการอธิบาย API ในวิธีที่คอมไพเลอร์สามารถช่วยบังคับใช้ มันไม่ได้มีเจตนาที่จะป้องกันการเข้าถึงชนิดBarโดยผู้ใช้Fooจึงไม่กีดขวาง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
และเมื่อต้องการดูว่าความเป็นส่วนตัวของชื่อไม่มีอะไรเกี่ยวข้องกับชนิดนั้นให้เพิ่มpublic: typedef Bar return_type_from_Baz;ไปยังคลาสFooในคำถาม ตอนนี้ชนิดสามารถระบุได้โดยชื่อสาธารณะแม้จะถูกกำหนดในส่วนส่วนตัวของชั้นเรียน
Steve Jessop

1
ที่จะทำซ้ำ @ จุดของสตีฟ: ตัวระบุการเข้าถึงสำหรับชื่อมีอะไรจะทำอย่างไรกับมันชนิดเท่าที่เห็นโดยการเพิ่มprivate: typedef Bar return_type_from_Baz;ที่จะFooเป็นแสดงให้เห็นถึง typedefตัวระบุจะไม่สนใจการเข้าถึงตัวระบุสาธารณะและส่วนตัว
damienh

มันไม่สมเหตุสมผลสำหรับฉัน ชื่อประเภทที่เป็นเพียงนามแฝงสำหรับประเภทที่เกิดขึ้นจริง มันจะมีอะไรสำคัญว่าถ้าผมเรียกมันBarหรือSomeDeducedType? ไม่ใช่ว่าฉันจะใช้มันเพื่อไปหาสมาชิกส่วนตัวclass Fooหรืออะไรก็ได้
einpoklum

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

คำถามนี้ได้รับการตอบรับอย่างดีจากทั้งเย็นและ R. Martinho Fernandes

ฉันไม่สามารถทิ้งโอกาสที่จะตอบคำถามด้วยการเปรียบเทียบ Harry Potter:

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;นั่นคือสิ่งที่ขาดหายไปหรือ
เควนติน

@Quentin คุณถูกต้องอย่างแน่นอน! เพื่อความสมบูรณ์เราควรเพิ่มfriend class Dumbledore;;)
jpihl

แฮร์รี่ไม่ได้แสดงว่าเขาไม่กลัวด้วยการโทร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.