เนมสเปซที่ไม่มีชื่อ / ไม่ระบุชื่อเปรียบเทียบกับฟังก์ชั่นแบบคงที่


507

คุณลักษณะของ C ++ คือความสามารถในการสร้างเนมสเปซที่ไม่มีชื่อ (ไม่ระบุชื่อ) เช่น:

namespace {
    int cannotAccessOutsideThisFile() { ... }
} // namespace

คุณอาจคิดว่าฟีเจอร์ดังกล่าวจะไร้ประโยชน์เนื่องจากคุณไม่สามารถระบุชื่อของเนมสเปซจึงเป็นไปไม่ได้ที่จะเข้าถึงสิ่งต่าง ๆ ภายในจากภายนอก แต่เหล่านี้ namespaces ชื่อเป็นเข้าถึงภายในไฟล์ที่พวกเขากำลังสร้างขึ้นในขณะที่ถ้าคุณมีนัยใช้ข้อให้กับพวกเขา

คำถามของฉันคือทำไมหรือเมื่อไรที่สิ่งนี้ดีกว่าที่จะใช้ฟังก์ชั่นแบบคงที่? หรือว่าพวกเขากำลังทำสิ่งเดียวกันแน่นอนสองวิธี?


13
ใน C ++ 11 การใช้งานของstaticในบริบทนี้ถูกundeprecated ; แม้จะไม่มีชื่อ namespace เป็นทางเลือกที่ดีกว่าstaticที่มีอยู่กรณีที่มันล้มเหลวเมื่อstaticมาเพื่อช่วยเหลือ
legends2k

คำตอบ:


332

C ++ Standard อ่านในส่วน 7.3.1.1 เนมสเปซที่ไม่มีชื่อวรรค 2:

การใช้คำสำคัญคงที่จะเลิกใช้เมื่อประกาศวัตถุในขอบเขต namespace, unnamed-namespace ให้ทางเลือกที่เหนือกว่า

สแตติกใช้กับชื่อของวัตถุฟังก์ชันและสหภาพที่ไม่ระบุชื่อเท่านั้นไม่พิมพ์ประกาศ

แก้ไข:

การตัดสินใจยกเลิกการใช้คำหลักแบบคงที่นี้ (ส่งผลต่อการมองเห็นการประกาศตัวแปรในหน่วยการแปล) ได้รับการย้อนกลับ ( ref ) ในกรณีนี้การใช้สแตติกหรือเนมสเปซที่ไม่มีชื่อจะกลับมาเป็นหลักสองวิธีในการทำสิ่งเดียวกัน สำหรับการอภิปรายเพิ่มเติมโปรดดูนี้คำถาม SO

เนมสเปซที่ไม่มีชื่อยังคงมีข้อดีของการอนุญาตให้คุณกำหนดประเภทหน่วยการแปลท้องถิ่น โปรดดูคำถาม SO นี้สำหรับรายละเอียดเพิ่มเติม

เครดิตไปที่Mike Percyซึ่งทำให้เรื่องนี้เป็นที่สนใจของฉัน


39
Head Geek ถามเกี่ยวกับคำหลักคงที่ที่ใช้กับฟังก์ชั่นเท่านั้น คีย์เวิร์ดแบบสแตติกใช้กับเอนทิตีที่ประกาศในขอบเขตเนมสเปซระบุการเชื่อมโยงภายใน เอนทิตีที่ประกาศในเนมสเปซที่ไม่ระบุชื่อมีการเชื่อมโยงภายนอก (C ++ / 3.5) อย่างไรก็ตามรับประกันว่าจะอยู่ในขอบเขตที่มีชื่อไม่ซ้ำกัน การไม่ระบุชื่อของเนมสเปซที่ไม่มีชื่อนี้จะซ่อนการประกาศอย่างมีประสิทธิภาพทำให้สามารถเข้าถึงได้จากภายในหน่วยการแปลเท่านั้น คำสั่งหลังทำงานอย่างมีประสิทธิภาพในลักษณะเดียวกับคำหลักคงที่
mloskot

5
ข้อเสียเปรียบของการเชื่อมโยงภายนอกคืออะไร? สิ่งนี้จะส่งผลกระทบต่อการขีดเส้นใต้หรือไม่
อเล็กซ์

17
ผู้ที่ออกแบบ C ++ ที่กล่าวว่าคำหลักคงที่ถูกคัดค้านอาจไม่เคยทำงานกับรหัส C ขนาดใหญ่ในระบบโลกแห่งความเป็นจริงที่มีขนาดใหญ่ ... บล็อก)
Calmarius

23
เนื่องจากคำตอบนี้ปรากฏขึ้นบน Google ว่าเป็นผลลัพธ์อันดับต้น ๆ สำหรับ "namespace ที่ไม่ระบุชื่อ c ++" ดังนั้นจึงควรสังเกตว่าการใช้งานสแตติกไม่เลิกใช้แล้ว ดูstackoverflow.com/questions/4726570/…และopen-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1012สำหรับข้อมูลเพิ่มเติม
Michael Percy

2
@ErikAronesty นั่นฟังดูผิด คุณมีตัวอย่างที่ทำซ้ำได้หรือไม่? ตั้งแต่ C ++ 11 - และแม้กระทั่งก่อนหน้านั้นในคอมไพเลอร์บางตัว - ชื่อที่ไม่มีnamespaceนัยมีการเชื่อมโยงภายในดังนั้นจึงไม่มีความแตกต่าง ปัญหาใด ๆ ที่อาจเกิดขึ้นจากการใช้ถ้อยคำที่ไม่ดีก่อนหน้านี้ได้รับการแก้ไขโดยทำให้ข้อกำหนดนี้ใน C ++ 11
underscore_d

73

การวางวิธีในเนมสเปซที่ไม่ระบุชื่อจะป้องกันคุณจากการละเมิดกฎข้อกำหนดหนึ่งคำโดยไม่ตั้งใจทำให้คุณไม่ต้องกังวลเกี่ยวกับการตั้งชื่อวิธีการช่วยเหลือเช่นเดียวกับวิธีอื่นที่คุณอาจเชื่อมโยง

และตามที่ลุคระบุไว้เนมสเปซที่ไม่ระบุชื่อเป็นที่ต้องการโดยมาตรฐานเหนือสมาชิกแบบคงที่


2
ฉันหมายถึงฟังก์ชั่นสแตนด์อะโลนแบบคงที่ (เช่นฟังก์ชั่นกำหนดขอบเขตไฟล์) ไม่ใช่ฟังก์ชั่นสมาชิกแบบคงที่ ฟังก์ชันแบบสแตนด์อโลนแบบคงที่จะเหมือนกับฟังก์ชันในเนมสเปซที่ไม่มีชื่อดังนั้นคำถาม
Head Geek

2
อา; ดี ODR ยังคงใช้ แก้ไขเพื่อลบย่อหน้า
hazzen

อย่างที่ฉันได้รับ ODR สำหรับฟังก์ชั่นคงที่ไม่ทำงานเมื่อมันถูกกำหนดในส่วนหัวและส่วนหัวนี้รวมอยู่ในหน่วยการแปลมากกว่าหนึ่งหน่วยใช่มั้ย ในกรณีนี้คุณจะได้รับฟังก์ชั่นเดียวกันหลายชุด
Andriy Tylychko

@Andy T: คุณไม่เห็น "หลายคำจำกัดความ" ในกรณีที่มีส่วนหัวรวมอยู่ด้วย ตัวประมวลผลล่วงหน้าดูแลมัน เว้นแต่จะมีความต้องการในการศึกษาผลลัพธ์ที่ preprocessor ได้สร้างขึ้นซึ่งสำหรับผมแล้วมันค่อนข้างแปลกใหม่และหายาก นอกจากนี้ยังมีแนวปฏิบัติที่ดีในการรวม "guards" ในไฟล์ส่วนหัวเช่น: "#ifndef SOME_GUARD - #define SOME_GUARD ... " ซึ่งควรป้องกัน preprocessor ไม่ให้รวมส่วนหัวเดียวกันสองครั้ง
Nikita Vorontsov

@NikitaVorontsov ตัวป้องกันอาจป้องกันไม่ให้รวมส่วนหัวเดียวกันลงในหน่วยการแปลเดียวกัน แต่อนุญาตให้มีข้อกำหนดจำนวนมากในหน่วยการแปลที่แตกต่างกัน นี่อาจทำให้เกิดข้อผิดพลาด "linkers หลายคำจำกัดความ" ลงบรรทัด
อเล็กซ์

37

มีกรณีขอบหนึ่งที่คงที่มีผลที่น่าแปลกใจ (อย่างน้อยก็สำหรับฉัน) สถานะ C ++ 03 มาตรฐานใน 14.6.4.2/1:

สำหรับการเรียกใช้ฟังก์ชันที่ขึ้นอยู่กับพารามิเตอร์เทมเพลตหากชื่อฟังก์ชั่นเป็นunqualified-idแต่ไม่ใช่เท็มเพลตไอดีฟังก์ชั่นผู้สมัครจะพบโดยใช้กฎการค้นหาปกติ (3.4.1, 3.4.2) ยกเว้นว่า:

  • สำหรับส่วนของการค้นหาโดยใช้การค้นหาชื่ออย่างไม่มีเงื่อนไข (3.4.1) จะพบเฉพาะการประกาศฟังก์ชันที่มีการเชื่อมโยงภายนอกจากบริบทนิยามเทมเพลตเท่านั้น
  • สำหรับส่วนของการค้นหาโดยใช้เนมสเปซที่เชื่อมโยง (3.4.2) จะมีเพียงการประกาศฟังก์ชันที่มีการเชื่อมโยงภายนอกที่พบในบริบทข้อกำหนดเทมเพลตหรือบริบทการสร้างอินสแตนซ์เทมเพลต

...

รหัสด้านล่างจะโทรfoo(void*)และไม่ได้foo(S const &)ตามที่คุณคาดหวัง

template <typename T>
int b1 (T const & t)
{
  foo(t);
}

namespace NS
{
  namespace
  {
    struct S
    {
    public:
      operator void * () const;
    };

    void foo (void*);
    static void foo (S const &);   // Not considered 14.6.4.2(b1)
  }

}

void b2()
{
  NS::S s;
  b1 (s);
}

ในตัวเองนี่อาจไม่ใช่ข้อตกลงที่ยิ่งใหญ่ แต่จะเน้นว่าสำหรับคอมไพเลอร์ C ++ ที่เข้ากันได้เต็มรูปแบบ (เช่นที่รองรับการสนับสนุนexport) staticคำหลักจะยังคงมีฟังก์ชั่นที่ไม่สามารถใช้ได้ในทางอื่น

// bar.h
export template <typename T>
int b1 (T const & t);

// bar.cc
#include "bar.h"
template <typename T>
int b1 (T const & t)
{
  foo(t);
}

// foo.cc
#include "bar.h"
namespace NS
{
  namespace
  {
    struct S
    {
    };

    void foo (S const & s);  // Will be found by different TU 'bar.cc'
  }
}

void b2()
{
  NS::S s;
  b1 (s);
}

วิธีเดียวที่จะทำให้แน่ใจว่าฟังก์ชันในเนมสเปซที่ไม่มีชื่อของเราจะไม่พบในเทมเพลตที่ใช้ ADL คือการสร้างมันstaticขึ้นมา

อัพเดทสำหรับ Modern C ++

ตั้งแต่ C ++ '11 สมาชิกของเนมสเปซที่ไม่มีชื่อมีการเชื่อมโยงภายในโดยนัย (3.5 / 4):

เนมสเปซที่ไม่มีชื่อหรือเนมสเปซที่ประกาศโดยตรงหรือโดยอ้อมภายในเนมสเปซที่ไม่มีชื่อมีการเชื่อมโยงภายใน

แต่ในเวลาเดียวกัน 14.6.4.2/1 ได้รับการปรับปรุงเพื่อลบการกล่าวถึงการเชื่อมโยง (สิ่งนี้นำมาจาก C ++ '14):

สำหรับการเรียกใช้ฟังก์ชั่นที่ postfix-expression เป็นชื่อที่ต้องพึ่งพาฟังก์ชันของตัวเลือกจะถูกพบโดยใช้กฎการค้นหาปกติ (3.4.1, 3.4.2) ยกเว้นว่า:

  • สำหรับส่วนของการค้นหาโดยใช้การค้นหาชื่ออย่างไม่มีเงื่อนไข (3.4.1) จะพบเฉพาะการประกาศฟังก์ชันจากบริบทการกำหนดเทมเพลต

  • สำหรับส่วนของการค้นหาโดยใช้เนมสเปซที่เชื่อมโยง (3.4.2) จะพบเฉพาะการประกาศฟังก์ชันที่พบในบริบทการกำหนดเทมเพลตหรือบริบทการสร้างอินสแตนซ์เทมเพลต

ผลลัพธ์คือความแตกต่างนี้ระหว่างสมาชิกเนมสเปซคงที่และไม่มีชื่อ


3
คำหลักส่งออกควรจะไม่เย็นชาใช่หรือไม่ คอมไพเลอร์เท่านั้นที่สนับสนุน "ส่งออก" เป็นคนทดลองและเว้นแต่ความผิด "ส่งออก" จะไม่ได้ถูกนำมาใช้ในคนอื่น ๆ เพราะผลข้างเคียงที่ไม่คาดคิด (นอกจากจะไม่ทำมันก็ถูกคาดว่าจะ)
paercebal

2
ดูบทความของ Herb Sutter ใน subjet: gotw.ca/publications/mill23-x.htm
paercebal

3
ส่วนหน้าจาก Edison Design Group (EDG) เป็นอะไรก็ได้นอกจากการทดลอง มันเกือบจะเป็นมาตรฐานที่เป็นไปตามมาตรฐาน C ++ มากที่สุดในโลก คอมไพเลอร์ Intel C ++ ใช้ EDG
Richard Corden

1
ฟีเจอร์ C ++ นั้นไม่มี 'ผลข้างเคียงที่ไม่คาดคิด'? ในกรณีของการส่งออกเป็นฟังก์ชั่นเนมสเปซที่ไม่มีชื่อจะพบได้จาก TU ที่แตกต่าง - นั่นคือเหมือนกับว่าคุณรวมคำนิยามเทมเพลตโดยตรง มันน่าแปลกใจมากถ้ามันไม่เป็นแบบนี้!
Richard Corden

ฉันคิดว่าคุณพิมพ์ผิดที่นั่น - สำหรับNS::Sการทำงานไม่Sจำเป็นต้องอยู่ข้างในnamespace {}ใช่ไหม?
Eric

12

ฉันเพิ่งเริ่มแทนที่คำหลักคงที่ด้วย namespaces ที่ไม่ระบุชื่อในรหัสของฉัน แต่ทันทีพบปัญหาที่ตัวแปรใน namespace ไม่สามารถใช้ได้อีกต่อไปสำหรับการตรวจสอบในการดีบักเกอร์ของฉัน ฉันใช้ VC60 ดังนั้นฉันไม่รู้ว่านั่นเป็นปัญหากับตัวแก้จุดบกพร่องอื่นหรือไม่ วิธีแก้ปัญหาของฉันคือกำหนด namespace 'โมดูล' ที่ฉันให้ชื่อของไฟล์ cpp ของฉัน

ตัวอย่างเช่นในไฟล์ XmlUtil.cpp ของฉันฉันจะกำหนดเนมสเปซXmlUtil_I { ... }สำหรับตัวแปรและฟังก์ชั่นโมดูลทั้งหมดของฉัน ด้วยวิธีนี้ฉันสามารถใช้การXmlUtil_I::รับรองในโปรแกรมดีบั๊กเพื่อเข้าถึงตัวแปร ในกรณีนี้_Iความแตกต่างจาก namespace สาธารณะเช่นXmlUtilที่ฉันอาจต้องการใช้ที่อื่น

ฉันคิดว่าข้อเสียที่เป็นไปได้ของวิธีนี้เมื่อเทียบกับแบบไม่ระบุชื่อจริงๆคือใครบางคนอาจละเมิดขอบเขตคงที่ที่ต้องการโดยใช้ตัวระบุเนมสเปซในโมดูลอื่น ๆ ฉันไม่รู้ว่านี่เป็นปัญหาใหญ่หรือไม่


7
ฉันได้ทำเช่นนี้ แต่ด้วย#if DEBUG namespace BlahBlah_private { #else namespace { #endifดังนั้น "module namespace" จะปรากฏเฉพาะในการตรวจแก้จุดบกพร่องสร้างและ namespace ที่ไม่ระบุชื่อจริงถูกนำมาใช้เป็นอย่างอื่น มันจะดีถ้า debuggers ให้วิธีที่ดีในการจัดการเรื่องนี้ Doxygen ก็สับสนเช่นกัน
Kristopher Johnson

4
เนมสเปซที่ไม่ได้ตั้งชื่อนั้นไม่สามารถทดแทนสแตติกได้ คงหมายถึง "จริงๆนี้ไม่เคยได้รับการเชื่อมโยง ouside ของม ธ ." Namespace ที่ไม่มีชื่อหมายถึง "มันยังถูกส่งออกเป็นชื่อสุ่มในกรณีที่ถูกเรียกจากคลาสแม่ที่อยู่นอก TU" ...
Erik Aronesty

7

การใช้คำหลักคงที่สำหรับวัตถุประสงค์นั้นถูกคัดค้านโดยมาตรฐาน C ++ 98 ปัญหาเกี่ยวกับสแตติกคือมันไม่ได้ใช้กับการกำหนดประเภท นอกจากนี้ยังเป็นคำหลักที่มากเกินไปที่ใช้ในวิธีที่แตกต่างกันในบริบทที่แตกต่างกันดังนั้นเนมสเปซที่ไม่มีชื่อจึงทำให้สิ่งต่าง ๆ ง่ายขึ้นเล็กน้อย


1
หากคุณต้องการใช้ประเภทเฉพาะในหน่วยการแปลเดียวให้ประกาศในไฟล์. cpp จะไม่สามารถเข้าถึงได้จากหน่วยการแปลอื่น ๆ
Calmarius

4
คุณคิดว่าใช่มั้ย แต่หากหน่วยการแปลอื่น (= cpp-file) ในแอปพลิเคชันเดียวกันเคยประกาศประเภทที่มีชื่อเดียวกันคุณจะพบกับปัญหาที่ค่อนข้างยากต่อการดีบัก :-) ตัวอย่างเช่นคุณอาจจบลงด้วยสถานการณ์ที่ใช้ vtable สำหรับประเภทใดประเภทหนึ่งเมื่อเรียกวิธีการอื่น ๆ
avl_sweden

1
ไม่เลิกอีกแล้ว และประเภท defs ไม่ถูกส่งออกดังนั้นจึงไม่มีความหมาย สถิตมีประโยชน์สำหรับฟังก์ชั่นแบบสแตนด์อโลนและ vars ทั่วโลก เนมสเปซที่ไม่มีชื่อนั้นมีประโยชน์สำหรับคลาส
Erik Aronesty

6

จากประสบการณ์ฉันจะทราบว่าในขณะที่มันเป็นวิธี C ++ ที่จะนำฟังก์ชั่นเดิมคงที่ใน namespace ที่ไม่ระบุชื่อบางครั้งคอมไพเลอร์รุ่นเก่าอาจมีปัญหากับเรื่องนี้ ขณะนี้ฉันทำงานร่วมกับคอมไพเลอร์สองสามตัวสำหรับแพลตฟอร์มเป้าหมายของเราและคอมไพเลอร์ Linux ที่ทันสมัยกว่าก็ใช้ได้กับการวางฟังก์ชันในเนมสเปซที่ไม่ระบุชื่อ

แต่คอมไพเลอร์รุ่นเก่าที่ทำงานบน Solaris ซึ่งเราจะนำไปสู่จนกว่าจะมีการเผยแพร่ในอนาคตที่ไม่ระบุรายละเอียดบางครั้งจะยอมรับและบางครั้งการตั้งค่าสถานะเป็นข้อผิดพลาด ข้อผิดพลาดที่ไม่ได้เป็นสิ่งที่ผมกังวลก็คือสิ่งที่มันอาจจะทำเมื่อมันยอมรับมัน ดังนั้นจนกว่าเราจะทันสมัยไปทั่วกระดานเรายังคงใช้ฟังก์ชั่นแบบคงที่


3

นอกจากนี้หากมีการใช้คำหลักคงที่ในตัวแปรเช่นตัวอย่างนี้:

namespace {
   static int flag;
}

มันจะไม่เห็นในไฟล์การทำแผนที่


7
จากนั้นคุณไม่จำเป็นต้องใช้เนมสเปซที่ไม่ระบุชื่อเลย
Calmarius

2

ความแตกต่างเฉพาะของคอมไพเลอร์ระหว่างเนมสเปซที่ไม่ระบุชื่อและฟังก์ชั่นแบบคงที่สามารถเห็นได้รวบรวมรหัสต่อไปนี้

#include <iostream>

namespace
{
    void unreferenced()
    {
        std::cout << "Unreferenced";
    }

    void referenced()
    {
        std::cout << "Referenced";
    }
}

static void static_unreferenced()
{
    std::cout << "Unreferenced";
}

static void static_referenced()
{
    std::cout << "Referenced";
}

int main()
{
    referenced();
    static_referenced();
    return 0;
}

รวบรวมรหัสนี้กับ VS 2017 (ระบุธงเตือนระดับ 4 / W4 เพื่อเปิดใช้งานการเตือน C4505: ฟังก์ชั่นภายในที่ไม่ได้อ้างอิงถูกลบ ) และ gcc 4.9 ด้วย -Wunused-function หรือ -Wall flag แสดงว่า VS 2017 จะสร้างคำเตือนสำหรับเท่านั้น ฟังก์ชั่นคงที่ไม่ได้ใช้ gcc 4.9 ขึ้นไปเช่นเดียวกับเสียงดังกราว 3.3 และสูงกว่าจะสร้างคำเตือนสำหรับฟังก์ชันที่ไม่ได้รับการอ้างอิงในเนมสเปซและยังเป็นคำเตือนสำหรับฟังก์ชันคงที่ที่ไม่ได้ใช้

การสาธิตสดของ gcc 4.9 และ MSVC 2017


2

ส่วนตัวแล้วฉันชอบฟังก์ชั่นคงที่มากกว่าเนมสเปซที่ไม่มีชื่อด้วยเหตุผลต่อไปนี้:

  • ชัดเจนและชัดเจนจากนิยามฟังก์ชั่นเพียงอย่างเดียวว่าเป็นแบบส่วนตัวไปยังหน่วยการแปลที่รวบรวม ด้วยเนมสเปซที่ไม่มีชื่อคุณอาจต้องเลื่อนและค้นหาเพื่อดูว่าฟังก์ชั่นนั้นอยู่ในเนมสเปซหรือไม่

  • ฟังก์ชั่นในเนมสเปซอาจได้รับการปฏิบัติเหมือนภายนอกโดยคอมไพเลอร์ (รุ่นเก่า) บางตัว ใน VS2017 พวกเขายังคงภายนอก ด้วยเหตุนี้แม้ว่าฟังก์ชั่นจะอยู่ใน namespace ที่ไม่มีชื่อคุณอาจยังคงต้องการทำเครื่องหมายว่าเป็นแบบคงที่

  • ฟังก์ชั่นแบบคงที่มีพฤติกรรมคล้ายกันมากใน C หรือ C ++ ในขณะที่ namespaces ที่ไม่ระบุชื่อเป็น C ++ เท่านั้น เนมสเปซนิรนามยังเพิ่มระดับพิเศษในการเยื้องและฉันไม่ชอบ :)

ดังนั้นผมมีความสุขที่จะเห็นการใช้งานคงที่สำหรับฟังก์ชั่นที่ไม่ได้เลิกใช้อีกต่อไป


ฟังก์ชั่นใน namespaces ที่ไม่ระบุชื่อควรจะมีการเชื่อมโยงภายนอก พวกเขากำลังยุ่งเหยิงเพื่อทำให้พวกเขามีเอกลักษณ์ เฉพาะstaticคำหลักใช้การเชื่อมโยงท้องถิ่นกับฟังก์ชั่น นอกจากนี้แน่นอนมีเพียงคนบ้าเพ้อเจ้อจริงจะเพิ่มการเยื้องสำหรับ namespaces?
Roflcopter4

0

เมื่อได้เรียนรู้คุณลักษณะนี้เพียงแค่ตอนนี้ในขณะที่อ่านคำถามของคุณฉันสามารถคาดเดาได้เท่านั้น ดูเหมือนว่าจะมีข้อดีหลายประการสำหรับตัวแปรแบบคงที่ระดับไฟล์:

  • เนมสเปซที่ไม่ระบุชื่อสามารถซ้อนกันได้ภายในอีกระดับหนึ่งซึ่งให้การป้องกันหลายระดับซึ่งสัญลักษณ์ไม่สามารถหนีได้
  • สามารถวางเนมสเปซที่ไม่ระบุชื่อหลายรายการในไฟล์ต้นฉบับเดียวกันโดยสร้างขอบเขตระดับคงที่ที่แตกต่างกันภายในไฟล์เดียวกัน

ฉันสนใจที่จะเรียนรู้ถ้ามีใครใช้เนมสเปซนิรนามในรหัสจริง


4
การคาดเดาที่ดี แต่ผิด ขอบเขตของเนมสเปซเหล่านี้มีความกว้างของไฟล์
Konrad Rudolph

ไม่ถูกต้องหากคุณกำหนดเนมสเปซที่ไม่ระบุตัวตนภายในเนมสเปซอื่นมันยังคงเป็นไฟล์กว้างเท่านั้นและจะเห็นได้ว่าอยู่ในเนมสเปซนั้นเท่านั้น ลองมัน.
Greg Rogers

ฉันอาจจะผิด แต่ฉันเดาว่าไม่มันไม่ใช่ไฟล์กว้าง: มันสามารถเข้าถึงได้เฉพาะรหัสหลังจาก namespace ที่ไม่ระบุชื่อ นี่คือสิ่งที่บอบบางและโดยปกติแล้วฉันไม่ต้องการทำให้เกิดมลภาวะแหล่งที่มากับเนมสเปซที่ไม่ระบุชื่อหลายรายการ ... ถึงกระนั้นก็สามารถใช้ประโยชน์ได้
paercebal

0

ความแตกต่างคือชื่อของตัวระบุที่ mangled ( _ZN12_GLOBAL__N_11bEvs _ZL1bซึ่งไม่สำคัญจริงๆ แต่ทั้งคู่ถูกรวมเข้ากับสัญลักษณ์ท้องถิ่นในตารางสัญลักษณ์ (ไม่มี.globalคำสั่ง asm)

#include<iostream>
namespace {
   int a = 3;
}

static int b = 4;
int c = 5;

int main (){
    std::cout << a << b << c;
}

        .data
        .align 4
        .type   _ZN12_GLOBAL__N_11aE, @object
        .size   _ZN12_GLOBAL__N_11aE, 4
_ZN12_GLOBAL__N_11aE:
        .long   3
        .align 4
        .type   _ZL1b, @object
        .size   _ZL1b, 4
_ZL1b:
        .long   4
        .globl  c
        .align 4
        .type   c, @object
        .size   c, 4
c:
        .long   5
        .text

สำหรับเนมสเปซที่ไม่ระบุชื่อแบบซ้อน:

namespace {
   namespace {
       int a = 3;
    }
}

        .data
        .align 4
        .type   _ZN12_GLOBAL__N_112_GLOBAL__N_11aE, @object
        .size   _ZN12_GLOBAL__N_112_GLOBAL__N_11aE, 4
_ZN12_GLOBAL__N_112_GLOBAL__N_11aE:
        .long   3
        .align 4
        .type   _ZL1b, @object
        .size   _ZL1b, 4

เนมสเปซที่ไม่ระบุชื่อระดับที่ 1 ทั้งหมดในหน่วยการแปลจะถูกรวมเข้าด้วยกัน, เนมสเปซที่ไม่ระบุตัวตนระดับที่ 2 ทั้งหมดในหน่วยการแปลจะรวมกัน

คุณสามารถมีเนมสเปซซ้อน (แบบอินไลน์) ในเนมสเปซที่ไม่ระบุชื่อ

namespace {
   namespace A {
       int a = 3;
    }
}

        .data
        .align 4
        .type   _ZN12_GLOBAL__N_11A1aE, @object
        .size   _ZN12_GLOBAL__N_11A1aE, 4
_ZN12_GLOBAL__N_11A1aE:
        .long   3
        .align 4
        .type   _ZL1b, @object
        .size   _ZL1b, 4

which for the record demangles as:
        .data
        .align 4
        .type   (anonymous namespace)::A::a, @object
        .size   (anonymous namespace)::A::a, 4
(anonymous namespace)::A::a:
        .long   3
        .align 4
        .type   b, @object
        .size   b, 4

คุณสามารถมีเนมสเปซอินไลน์แบบไม่ระบุชื่อ แต่เท่าที่ฉันสามารถบอกได้inlineในเนมสเปซที่ไม่ระบุตัวตนมีผล 0

inline namespace {
   inline namespace {
       int a = 3;
    }
}

_ZL1b: _Zหมายความว่านี่เป็นตัวระบุที่ถูกบันทึกไว้ หมายความว่ามันเป็นสัญลักษณ์ท้องถิ่นผ่านL คือความยาวของตัวระบุและตัวระบุstatic1bb

_ZN12_GLOBAL__N_11aE _Zหมายความว่านี่เป็นตัวบ่งชี้ที่ยุ่งเหยิง Nหมายถึงนี้เป็น namespace 12คือความยาวของชื่อ namespace ที่ไม่ระบุชื่อ_GLOBAL__N_1แล้วชื่อ namespace ที่ไม่ระบุชื่อ_GLOBAL__N_1แล้ว1คือความยาวของตัวระบุที่a, aเป็นตัวบ่งชี้aและEปิดตัวระบุที่อยู่ในใน namespace

_ZN12_GLOBAL__N_11A1aE เหมือนกับด้านบนยกเว้นมีเนมสเปซอีกระดับหนึ่งอยู่ 1A

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.