เหตุใดจึงใช้เนมสเปซที่ไม่มีชื่อและประโยชน์ของพวกเขาคืออะไร


242

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

// newusertype.cc
namespace {
  const int SIZE_OF_ARRAY_X;
  const int SIZE_OF_ARRAY_Y;
  bool getState(userType*,otherUserType*);
}

newusertype::newusertype(...) {...

อะไรคือข้อควรพิจารณาในการออกแบบที่อาจทำให้เกิดการใช้เนมสเปซที่ไม่มีชื่อ ข้อดีและข้อเสียคืออะไร?

คำตอบ:


189

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

namespace unique { /* empty */ }
using namespace unique;
namespace unique { /* namespace body. stuff in here */ }

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

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

namespace { int a1; }
static int a2;

ทั้งสองaเป็นหน่วยแปลในท้องถิ่นและจะไม่ปะทะกันในเวลาลิงก์ แต่ความแตกต่างคือa1ในเนมสเปซที่ไม่ระบุชื่อจะได้รับชื่อที่ไม่ซ้ำกัน

อ่านบทความที่ยอดเยี่ยมที่ comeau-computing ทำไมเนมสเปซที่ไม่มีชื่อใช้แทนสแตติก ( กระจก Archive.org )


staticคุณอธิบายความสัมพันธ์กับ คุณช่วยเปรียบเทียบด้วยได้__attribute__ ((visibility ("hidden")))มั้ย
phinz

74

การมีบางสิ่งบางอย่างในเนมสเปซที่ไม่ระบุชื่อหมายความว่ามันอยู่ในหน่วยการแปลนี้(ไฟล์. cpp และรวมอยู่ด้วย) ซึ่งหมายความว่าหากมีการกำหนดสัญลักษณ์อื่นที่มีชื่อเดียวกันในที่อื่นจะไม่เป็นการฝ่าฝืนOne Definition Rule (ODR)

นี่เป็นวิธีเดียวกับ C ในการมีตัวแปรโกลบอลสแตติกหรือฟังก์ชันสแตติก แต่สามารถใช้สำหรับการกำหนดคลาสได้เช่นกัน (และควรใช้มากกว่าstaticใน C ++)

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

namespace __unique_compiler_generated_identifer0x42 {
    ...
}
using namespace __unique_compiler_generated_identifer0x42;

14

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

static int x;  // Correct 

แต่,

static class xyz {/*Body of class*/} //Wrong
static structure {/*Body of structure*/} //Wrong

แต่สามารถทำได้ด้วยเนมสเปซที่ไม่มีชื่อ ตัวอย่างเช่น,

 namespace {
           class xyz {/*Body of class*/}
           static structure {/*Body of structure*/}
  } //Correct

13

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

ตัวอย่างเช่นในระบบของฉันรหัสต่อไปนี้ใช้เวลาประมาณ 70% ของเวลาทำงานหากใช้เนมสเปซนิรนาม (x86-64 gcc-4.6.3 และ -O2) โปรดทราบว่ารหัสพิเศษใน add_val ทำให้คอมไพเลอร์ไม่ต้องการรวม มันสองครั้ง)

#include <iostream>

namespace {
  double a;
  void b(double x)
  {
    a -= x;
  }
  void add_val(double x)
  {
    a += x;
    if(x==0.01) b(0);
    if(x==0.02) b(0.6);
    if(x==0.03) b(-0.1);
    if(x==0.04) b(0.4);
  }
}

int main()
{
  a = 0;
  for(int i=0; i<1000000000; ++i)
    {
      add_val(i*1e-10);
    }
  std::cout << a << '\n';
  return 0;
}

5
ดีเกินไปที่จะเป็นจริง - ฉันลองส่วนนี้ใน gcc 4-1-2 โดยใช้การเพิ่มประสิทธิภาพ O3 โดยมีและไม่มีคำสั่ง namespace: -> มีเวลาเดียวกัน (3 วินาที, -O3 และ 4sec กับ -O3)
Theo

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

5
@Daniel: สิ่งที่ฉันหายไป? เมื่ออ่านแล้วคุณบอกว่าคุณเปรียบเทียบ-O3กับตัวคุณเองแล้วคุณบอกว่า 3 vs 4 วินาทีคือ "เวลาเดียวกัน" สิ่งเหล่านี้ไม่สมเหตุสมผล ฉันสงสัยว่าคำอธิบายที่แท้จริงจะเป็นเช่นไร แต่มันคืออะไร?
underscore_d

@underscore_d คำตอบสถานะ -O2 ถูกใช้ในทั้งสองกรณีไม่ใช่ -O3 ระดับการเพิ่มประสิทธิภาพที่แตกต่างกันอาจทำงานแตกต่างกัน นอกจากนี้คอมไพเลอร์รุ่นต่าง ๆ อาจทำงานต่างกัน (คำตอบอาจล้าสมัยนั่นคือ)
Paul Stelian

1
@ PaulStelian ฉันรู้ว่า แต่ดูเหมือนว่าค่อนข้างชัดเจนว่าฉันตอบว่าไม่ใช่คำตอบของ xioxox แต่เป็นการแสดงความคิดเห็นของ Theo (แม้ว่าชื่อของเขาจะเปลี่ยนไปหรือฉันสับสนอยู่บ้าง)
underscore_d

12

ตัวอย่างแสดงว่าคนในโครงการที่คุณเข้าร่วมไม่เข้าใจเนมสเปซที่ไม่ระบุชื่อ :)

namespace {
    const int SIZE_OF_ARRAY_X;
    const int SIZE_OF_ARRAY_Y;

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

    bool getState(userType*,otherUserType*);
}

และนี่คือการมองโลกในแง่ร้าย: getState()มีการเชื่อมโยงจากภายนอก โดยปกติแล้วจะเป็นการดีกว่าที่จะชอบการเชื่อมโยงแบบคงที่เนื่องจากจะไม่ทำให้ตารางสัญลักษณ์เป็นมลพิษ มันจะดีกว่าที่จะเขียน

static bool getState(/*...*/);

ที่นี่ ฉันตกหลุมพรางเดียวกัน (มีถ้อยคำตามมาตรฐานที่แนะนำว่าไฟล์ - สแตติกถูกคัดค้านอย่างไม่ระบุชื่อ) แต่การทำงานในโครงการ C ++ ขนาดใหญ่เช่น KDE คุณจะได้รับผู้คนจำนวนมากที่หันหัวของคุณอย่างถูกวิธี อีกรอบ :)


10
เนื่องจากเนมสเปซที่ไม่มีชื่อ c ++ 11 มีการเชื่อมโยงภายใน (ส่วน 3.5 ในมาตรฐานหรือen.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces )
Emile Vrijdags

11
"เหล่านี้ไม่จำเป็นต้องอยู่ใน namespace ที่ไม่ระบุชื่อ" เทคนิคแน่นอน - แต่ยังคงไม่เจ็บจะนำพวกเขาในหนึ่งเป็นตัวเตือนภาพของความหมายของพวกเขาและจะทำให้มัน (ยิ่งขึ้น) เล็กน้อยเพื่อลบconstNess ในภายหลังหากต้องการ ฉันสงสัยว่าหมายความว่าทีมของ OP "ไม่เข้าใจ" อะไรเลย! นอกจากนี้บิตเกี่ยวกับฟังก์ชั่นในเนมสเปซที่ไม่ระบุชื่อที่มีการเชื่อมโยงภายนอกนั้นไม่ถูกต้องใน C ++ 11 เป็นต้นไปตามที่ระบุไว้ โดยความเข้าใจของฉันพวกเขาแก้ไขปัญหาของข้อโต้แย้งแม่แบบก่อนหน้านี้ที่ต้องการการเชื่อมโยงภายนอกดังนั้นอาจอนุญาตให้เนมสเปซที่ไม่มีชื่อ (สามารถมีเทมเพลต args) ที่จะมีการเชื่อมโยงภายใน
underscore_d

11

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

มีข้อดีหรือข้อเสียไม่มากไปกว่า "ฉันต้องการให้ตัวแปรฟังก์ชันคลาส ฯลฯ เป็นแบบสาธารณะหรือส่วนตัว"


2
อาจมีความแตกต่างด้านประสิทธิภาพ - ดูคำตอบของฉันที่นี่ จะช่วยให้คอมไพเลอร์เพื่อเพิ่มประสิทธิภาพรหัสดีกว่า
xioxox

2
คุณมีประเด็น; อย่างน้อยที่สุดเท่าที่ C ++ ในวันนี้คือ อย่างไรก็ตาม C ++ 98 / C ++ 03 สิ่งจำเป็นต้องมีการเชื่อมโยงภายนอกเพื่อที่จะใช้เป็นอาร์กิวเมนต์แม่แบบ เนื่องจากสิ่งต่าง ๆ ในเนมสเปซที่ไม่ระบุชื่อนั้นมีอยู่ในรูปแบบอาร์กิวเมนต์ของเทมเพลตจึงจะมีลิงก์ภายนอก (อย่างน้อยใน pre-C ++ 11) แม้ว่าจะไม่มีวิธีการอ้างถึงพวกเขาจากภายนอกไฟล์ ฉันคิดว่าอาจมีความสามารถที่จะเหลวไหลในเรื่องนั้นเพราะมาตรฐานเพียงต้องการให้สิ่งต่าง ๆ ทำหน้าที่เสมือนว่ามีการบังคับใช้กฎ และบางครั้งก็เป็นไปได้ที่จะทำเช่นนั้นโดยไม่บังคับใช้กฎอย่างแท้จริง
Max Lybbert
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.