ใช้ std Namespace


110

ดูเหมือนจะมีมุมมองที่แตกต่างกันในการใช้ "การใช้" ที่เกี่ยวกับเนมสเปซมาตรฐาน

บางคนบอกว่าใช้ ' using namespace std' อีกคนบอกว่าอย่า แต่ใช้คำนำหน้าฟังก์ชัน std ที่จะใช้กับ ' std::' ในขณะที่คนอื่นบอกว่าให้ใช้สิ่งนี้:

using std::string;
using std::cout;
using std::cin;
using std::endl;
using std::vector;

สำหรับฟังก์ชันมาตรฐานทั้งหมดที่จะใช้

ข้อดีข้อเสียของแต่ละข้อคืออะไร?




คำตอบ:


131

ส่วนใหญ่ c ++ ผู้ใช้อ่านมีความสุขมากstd::string, std::vectorฯลฯ ในความเป็นจริงเห็นดิบvectorทำให้ฉันสงสัยว่านี้เป็นหรือแตกต่างกันที่ผู้ใช้กำหนดstd::vectorvector

using namespace std;ฉันเสมอกับการใช้ นำเข้าชื่อทุกประเภทไปยังเนมสเปซส่วนกลางและอาจทำให้เกิดความคลุมเครือที่ไม่ชัดเจนได้ทุกประเภท

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

ตัวอย่างคลาสสิกของความขัดแย้งของชื่อที่ไม่ต้องการมีดังต่อไปนี้ std::countลองจินตนาการว่าคุณเป็นมือใหม่และไม่ทราบเกี่ยวกับ ลองนึกภาพว่าคุณกำลังใช้อย่างอื่น<algorithm>หรือถูกดึงเข้ามาโดยส่วนหัวที่ดูเหมือนไม่เกี่ยวข้องกัน

#include <algorithm>
using namespace std;

int count = 0;

int increment()
{
    return ++count; // error, identifier count is ambiguous
}

โดยทั่วไปข้อผิดพลาดจะยาวและไม่เป็นมิตรเนื่องจากstd::countเป็นเทมเพลตที่มีประเภทซ้อนกันยาว

แม้ว่าจะไม่เป็นไรเนื่องจากstd::countเข้าสู่เนมสเปซส่วนกลางและจำนวนฟังก์ชันจะซ่อนไว้

#include <algorithm>
using namespace std;

int increment()
{
    static int count = 0;
    return ++count;
}

อาจจะน่าแปลกใจเล็กน้อยนี่ก็โอเค ตัวระบุที่นำเข้าในขอบเขตการประกาศจะปรากฏในเนมสเปซทั่วไปที่ล้อมรอบทั้งที่กำหนดและตำแหน่งที่นำเข้า ในคำอื่น ๆstd::countจะมองเห็นเป็นcountใน namespace ทั่วโลก incrementแต่เฉพาะภายใน

#include <algorithm>

int increment()
{
    using namespace std;
    static int count = 0;
    return ++count;
}

และด้วยเหตุผลที่คล้ายกันcountที่นี่มีความคลุมเครือ using namespace stdไม่ก่อให้เกิดstd::countซ่อนด้านนอกcountอย่างที่คาดไว้ using namespaceกฎหมายความว่าstd::countรูปลักษณ์ (ในincrementฟังก์ชั่น) ราวกับว่ามันถูกประกาศในขอบเขตทั่วโลกเช่นในขอบเขตเช่นเดียวกับint count = 0;และด้วยเหตุที่ก่อให้เกิดความคลุมเครือ

#include <algorithm>

int count = 0;

int increment()
{
    using namespace std;
    return ++count; // error ambiguous
}

21
แต่มันประเภทsooooง่ายมากโดยไม่ได้มาตรฐาน :: คำนำหน้า!
xtofl

69
@xtofl: ไม่มันไม่ได้ อักขระห้าตัวไม่เกี่ยวข้องกันเมื่อพิมพ์ แต่อักขระทั้งห้านี้อาจเกี่ยวข้องมากเมื่ออ่าน และความสะดวกในการอ่านนั้นมีค่ามากกว่าความสะดวกในการพิมพ์สำหรับซอร์สโค้ดเนื่องจากโค้ดนั้นอ่านได้มากกว่าการเขียน
sbi

3
คุณสามารถเพิ่มได้ว่าคำสั่งใช้ทำงานอย่างถูกต้องตามกฎขอบเขต
Martin York

2
@Martin York: อัปเดตด้วยตัวอย่างที่แสดงกฎการกำหนดขอบเขต @ Michael Burr: เนื้อหาไม่เลวร้ายนักสิ่งที่ฉันไม่ชอบจริงๆคือที่ข้อความแสดงข้อผิดพลาดสำหรับข้อผิดพลาดง่าย ๆ จะตีความได้ยากขึ้นมากหรือไม่เกิดขึ้นเลย ตัวอย่างเช่นหากเชื่อว่าฟังก์ชันอยู่ในขอบเขต แต่ไม่ใช่และฟังก์ชัน std :: แทนที่จะได้รับข้อผิดพลาด 'ไม่รู้จักตัวระบุ' ที่เป็นประโยชน์คุณมักจะลงเอยด้วยความคลุมเครือมากกว่า 'ไม่สามารถแปลงอาร์กิวเมนต์ได้ ข้อผิดพลาดรูปแบบ X 'หรือ' ไม่สามารถสร้างฟังก์ชันจากเทมเพลต ' ที่แย่กว่านั้นคือถ้ามีการเรียกใช้ฟังก์ชันที่ไม่ถูกต้อง หายาก แต่เกิดขึ้น
CB Bailey

5
using std::xxx;ดีนแปลกใจไม่มีใครกล่าวถึงเกี่ยวกับตัวเลือกของ มันไม่ได้ทำมลพิษ namespace เขียนรหัสจะสั้นลงและฉันคิดว่าcopyเป็นจำนวนมาก readale std::copyมากกว่า
legends2k

41

ไม่รวมพื้นฐาน (ต้องเพิ่ม std :: อยู่หน้าอ็อบเจ็กต์ / ฟังก์ชัน stl ทั้งหมดและมีโอกาสขัดแย้งน้อยลงหากคุณไม่มี 'ใช้เนมสเปซ std')

นอกจากนี้ยังเป็นที่น่าสังเกตว่าคุณไม่ควรใส่

using namespace std

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

ในบางกรณีการใช้สิ่งต่างๆเช่น

using std::swap

std::swapเช่นถ้ามีรุ่นเฉพาะของแลกเปลี่ยนคอมไพเลอร์จะใช้ที่มิฉะนั้นมันจะถอยกลับใน

หากคุณโทรstd::swapคุณจะใช้เวอร์ชันพื้นฐานเสมอซึ่งจะไม่เรียกเวอร์ชันที่ปรับให้เหมาะสม (หากมีอยู่)


10
+1 สำหรับการกล่าวถึงusing std::swap(ซึ่งเป็นสิ่งเดียวที่ฉันเคยใช้)
sbi

1
+1 สำหรับการกล่าวถึงที่u n sสามารถเผยแพร่ โปรดทราบว่ามันยังสามารถหนอนเข้าไปในส่วนหัวที่สร้างขึ้นอย่างถูกต้องได้เช่นกันพวกเขาจะต้องรวมไว้หลังส่วนหัวที่หลอกลวง
quamrana

1
แต่ถ้าคุณกำลังกำหนดswapหรือmove(หรือhash, lessฯลฯ ) เชี่ยวชาญคุณควรจะใส่ความเชี่ยวชาญที่เป็นnamespace stdอยู่แล้ว ตัวอย่างเช่น:namespace std {template<> class hash<X> {public: size_t operator()(const X&) const};} class X: {friend size_t std::hash<X>::operator()(const X&)};
AJMansfield

28

ขั้นแรกคำศัพท์บางคำ:

  • ใช้ประกาศ : using std::vector;
  • การใช้คำสั่ง : using namespace std;

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

using namespace std;

ในไฟล์. cpp ของคุณไม่ใช่ปัญหาจริงๆและหากปรากฎว่าเป็นเช่นนั้นก็จะอยู่ภายใต้การควบคุมของคุณโดยสมบูรณ์ (และยังสามารถกำหนดขอบเขตไปยังบล็อกใดบล็อกหนึ่งได้หากต้องการ) ฉันไม่เห็นเหตุผลของอนุภาคที่จะทำให้โค้ดยุ่งเหยิงด้วยstd::คุณสมบัติมากมาย - มันกลายเป็นสัญญาณรบกวนภาพมากมาย อย่างไรก็ตามหากคุณไม่ได้ใช้ชื่อทั้งหมดจากstdเนมสเปซในโค้ดของคุณฉันก็ไม่เห็นปัญหาในการละทิ้งคำสั่ง มันเป็น tautology - หากไม่จำเป็นต้องใช้คำสั่งก็ไม่จำเป็นต้องใช้มัน

ในทำนองเดียวกันหากคุณสามารถทำได้โดยใช้การประกาศโดยใช้ไม่กี่คำ (แทนที่จะใช้คำสั่ง ) สำหรับประเภท specfic ในstdเนมสเปซก็ไม่มีเหตุผลที่คุณไม่ควรมีเพียงชื่อเฉพาะที่นำเข้ามาในเนมสเปซปัจจุบัน ในทำนองเดียวกันฉันคิดว่ามันคงบ้าและการทำบัญชียุ่งยากที่จะต้องมีการประกาศการใช้ 25 หรือ 30 ครั้งเมื่อคำสั่งการใช้เดียวจะทำเคล็ดลับได้เช่นกัน

นอกจากนี้โปรดทราบว่ามีหลายครั้งที่คุณต้องใช้การประกาศใช้ อ้างถึง "รายการที่ 25 ของ Scott Meyers: พิจารณาการสนับสนุนสำหรับการแลกเปลี่ยนแบบไม่โยนทิ้ง" จาก C ++ ที่มีผลบังคับใช้รุ่นที่สาม เพื่อให้มีฟังก์ชันเทมเพลตทั่วไปใช้วิธีการสลับ 'ที่ดีที่สุด' สำหรับประเภทที่กำหนดพารามิเตอร์คุณต้องใช้ประโยชน์จากการประกาศโดยใช้และการค้นหาตามอาร์กิวเมนต์ (aka ADL หรือ Koenig lookup):

template< typename T >
void foo( T& x, T& y)
{
    using std::swap;     // makes std::swap available in this function

    // do stuff...

    swap( x, y);         // will use a T-specific swap() if it exists,
                         //  otherwise will use std::swap<T>()

    // ...
 }

ฉันคิดว่าเราควรดูสำนวนทั่วไปสำหรับภาษาต่างๆที่ใช้เนมสเปซอย่างมีนัยสำคัญ ตัวอย่างเช่น Java และ C # ใช้เนมสเปซในระดับมาก (เนื้อหามากกว่า C ++) วิธีที่ใช้บ่อยที่สุดชื่อภายในเนมสเปซในภาษาเหล่านั้นคือการนำชื่อเหล่านั้นเข้าสู่ขอบเขตปัจจุบันโดยรวมเทียบเท่ากับการใช้คำสั่ง สิ่งนี้ไม่ก่อให้เกิดปัญหาในวงกว้างและไม่กี่ครั้งที่เป็นปัญหาจะได้รับการจัดการบนพื้นฐาน 'ข้อยกเว้น' โดยจัดการกับชื่อที่เป็นปัญหาผ่านชื่อที่มีคุณสมบัติครบถ้วนหรือโดยการใช้นามแฝง - เช่นเดียวกับที่สามารถทำได้ใน C ++

Herb Sutter และ Andrei Alexandrescu กล่าวไว้ใน "รายการที่ 59: อย่าเขียนการใช้เนมสเปซในไฟล์ส่วนหัวหรือก่อน # รวม" ของหนังสือมาตรฐานการเข้ารหัส C ++: กฎ 101 ข้อหลักเกณฑ์และแนวทางปฏิบัติที่ดีที่สุด:

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

Stroupstrup มักจะอ้างว่า "อย่าทำให้เนมสเปซทั่วโลกเป็นมลพิษ" ใน "ภาษาโปรแกรม C ++ รุ่นที่สาม" อันที่จริงเขาพูดแบบนั้น (C.14 [15]) แต่อ้างถึงบทที่ C.10.1 ที่เขาพูดว่า:

การประกาศใช้เพิ่มชื่อให้กับขอบเขตโลคัล ใช้-สั่งไม่ได้; เพียงแค่แสดงชื่อที่สามารถเข้าถึงได้ในขอบเขตที่มีการประกาศ ตัวอย่างเช่น:

namespaceX {
    int i , j , k ;
}

int k ;
void f1()
{
    int i = 0 ;

    using namespaceX ; // make names from X accessible

    i++; // local i
    j++; // X::j
    k++; // error: X::k or global k ?

    ::k ++; // the global k

    X::k ++; // X’s k
}

void f2()
{
    int i = 0 ;

    using X::i ; // error: i declared twice in f2()
    using X::j ;
    using X::k ; // hides global k

    i++;
    j++; // X::j
    k++; // X::k
}

ชื่อที่ประกาศในเครื่อง (ประกาศโดยการประกาศธรรมดาหรือโดยการประกาศโดยใช้) จะซ่อนการประกาศที่ไม่ใช่คนในท้องถิ่นที่มีชื่อเดียวกันและตรวจพบการโอเวอร์โหลดของชื่อที่ผิดกฎหมาย ณ จุดประกาศ

หมายเหตุ: ข้อผิดพลาดคลุมเครือสำหรับในk++ f1()ชื่อส่วนกลางไม่ได้รับการกำหนดค่าตามความชอบเหนือชื่อจากเนมสเปซที่เข้าถึงได้ในขอบเขตส่วนกลาง สิ่งนี้ให้การป้องกันอย่างมีนัยสำคัญจากการปะทะกันของชื่อโดยบังเอิญและที่สำคัญ - ช่วยให้มั่นใจได้ว่าจะไม่มีข้อดีใด ๆ ที่จะได้รับจากการสร้างมลพิษให้กับเนมสเปซทั่วโลก

เมื่อไลบรารีที่ประกาศชื่อจำนวนมากสามารถเข้าถึงได้โดยใช้คำสั่งเป็นข้อได้เปรียบที่สำคัญที่การขัดแย้งกันของชื่อที่ไม่ได้ใช้จะไม่ถือว่าเป็นข้อผิดพลาด

...

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

และมีข้อได้เปรียบเช่นเดียวกับ 'ผู้ใช้ชื่อทั่วโลกที่ขี้เกียจ' อย่างไร? โดยการใช้ประโยชน์จากการใช้-สั่งซึ่งได้อย่างปลอดภัยทำให้ชื่อใน namespace สามารถใช้ได้กับขอบเขตปัจจุบัน

โปรดทราบว่ามีความแตกต่าง - ชื่อในstdเนมสเปซที่พร้อมใช้งานในขอบเขตที่มีการใช้คำสั่งใช้อย่างเหมาะสม (โดยการวางคำสั่งไว้หลัง#includes) จะไม่ก่อให้เกิดเนมสเปซส่วนกลาง เพียงแค่ทำให้ชื่อเหล่านั้นใช้งานได้ง่ายและมีการป้องกันการปะทะ


เกี่ยวกับจุดสุดท้ายของคุณ: Java และ C # ยังมีเนมสเปซที่ดีกว่ามาก หากทุกอย่างใน BCL อยู่ในระบบ "การใช้ระบบ" จะทำให้เกิดปัญหาพอ ๆ กับ "การใช้เนมสเปซ std"
Jeff Hardy

แต่โปรแกรม Java และ C # ที่ฉันเห็นมักจะนำมาในเนมสเปซทั้งหมดที่พวกเขาใช้ไม่ใช่แค่ "ระบบ" (หรือเทียบเท่า) ดังนั้นแทนที่จะใช้คำสั่งเดียวที่นำมาใช้ในชื่อทั้งหมดมี 5 หรือ 10 ที่ทำสิ่งเดียวกันมากหรือน้อย นอกจากนี้ยัง "ใช้เนมสเปซ std;" ทำให้เกิดปัญหามากขนาดนั้นจริงหรือ?
Michael Burr

ปัญหาคือ std มีชื่อสามัญมากเกินไปและการรวมส่วนหัวมาตรฐานหนึ่งอาจรวมถึงชื่ออื่น ๆ ทั้งหมด เราไม่สามารถควบคุมสิ่งที่นำเข้าได้ดีมีความเสี่ยงมากเกินไป ฉันไม่รู้เกี่ยวกับ Java และ C # มากพอ แต่ฉันรู้เกี่ยวกับ Ada ซึ่งมีระบบโมดูลที่ดีกว่า C ++ มากและโดยทั่วไปแล้วชื่อการนำเข้าจะขมวดคิ้ว โดยทั่วไปแล้วเรื่องแรกของหลักการตั้งชื่อ (ฉันเคยเห็นคนใช้คำนำหน้าเช่นเดียวกับเนมสเปซการไม่นำเข้าไม่สมเหตุสมผล) ตามสไตล์
AProgrammer

1
ฉันยังไม่มั่นใจว่านี่เป็นปัญหาในโลกแห่งความเป็นจริง ฉันเห็นคำสั่งการใช้ที่ใช้ตลอดเวลาโดยไม่มีข้อเสียที่รุนแรง แล้วอีกครั้งฉันไม่มีปัญหากับการไม่ใช้พวกเขา ฉันแค่ต้องการให้ตัวกำหนดstd::คุณสมบัติไม่เกะกะโค้ด - มีวิธีอื่นในการหลีกเลี่ยงสิ่งนั้น (การใช้การประกาศหรือการพิมพ์ดีดมักจะทำเคล็ดลับ)
Michael Burr

1
@AProgrammer: คุณพูดว่า "รายการเป็นตัวระบุตามธรรมชาติสำหรับการระบุรายการในตัวแปลเสียงกระเพื่อม" แต่การมี " using namespace std;" คำสั่งไม่ได้ป้องกันไม่ให้คุณประกาศตัวระบุธรรมชาติของคุณ ' list' แต่ถ้าคุณทำคุณจะไม่สามารถทำได้ ใช้งานได้นานขึ้นstd::listโดยไม่ผ่านการรับรอง ไม่ต่างอะไรกับถ้าไม่มีusing namespace std;คำสั่ง "" หรือฉันขาดอะไรไป?
Michael Burr

17

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

ในไฟล์การนำไปใช้งานตัวเลือกจะถูกตัดออกไปน้อยมาก

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

  • การใส่โดยใช้ std :: name; มีข้อได้เปรียบของความเรียบง่ายในการเขียนโดยไม่ต้องเสี่ยงต่อการนำเข้าสัญลักษณ์ที่ไม่รู้จัก ค่าใช้จ่ายคือคุณต้องนำเข้าสัญลักษณ์ที่ต้องการทั้งหมดอย่างชัดเจน

  • คุณสมบัติที่ชัดเจนเพิ่มความยุ่งเหยิงเล็กน้อย แต่ฉันคิดว่ามันเป็นปัญหาน้อยกว่าในการฝึกฝน

ในโครงการของฉันฉันใช้คุณสมบัติที่ชัดเจนสำหรับชื่อทั้งหมดฉันยอมรับการใช้ std :: name ฉันต่อสู้กับการใช้ namespace std (เรามีล่ามเสียงกระเพื่อมซึ่งมีประเภทรายการของตัวเองดังนั้นความขัดแย้งจึงเป็นสิ่งที่แน่นอน)

สำหรับเนมสเปซอื่นคุณต้องคำนึงถึงหลักการตั้งชื่อที่ใช้ด้วย ฉันรู้จักโครงการที่ใช้เนมสเปซ (สำหรับการกำหนดเวอร์ชัน) และคำนำหน้าชื่อ ทำแล้วเกือบจะไม่มีความเสี่ยงและไม่ได้ทำมันจะนำไปสู่รหัสมองโง่using namespace XPrefixNS::pfxMyFunction(...)

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


แก้ไข:

ในความคิดเห็น Michael Burr สงสัยว่าความขัดแย้งเกิดขึ้นในโลกแห่งความเป็นจริงหรือไม่ นี่คือตัวอย่างสดจริง เรามีภาษาเสริมด้วยคือภาษาถิ่นกระเพื่อม ล่ามของเรามีไฟล์ include ซึ่งประกอบด้วย lisp.h

typedef struct list {} list;

เราต้องรวมและปรับโค้ดบางอย่าง (ซึ่งฉันจะตั้งชื่อว่า "engine") ซึ่งมีลักษณะดังนี้:

#include <list>
...
using std::list;
...
void foo(list const&) {}

ดังนั้นเราจึงแก้ไขดังนี้:

#include <list>

#include "module.h"
...
using std::list;
...
void foo(list const&) {}

ดี. ทุกอย่างทำงานได้ หลายเดือนต่อมามีการแก้ไข "module.h" ให้รวม "list.h" การทดสอบผ่านไป "โมดูล" ไม่ได้รับการแก้ไขในลักษณะที่ส่งผลต่อ ABI ดังนั้นจึงสามารถใช้ไลบรารี "engine" ได้โดยไม่ต้องรวบรวมผู้ใช้ใหม่ การทดสอบการบูรณาการทำได้ดี เผยแพร่ "โมดูล" ใหม่แล้ว การคอมไพล์เครื่องยนต์ครั้งต่อไปพังเมื่อไม่ได้แก้ไขโค้ด


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

1
ฉันคิดว่าการพิมพ์ std :: เป็นราคาเล็กน้อยที่ต้องจ่ายเพื่อความชัดเจน
paoloricardo

4
@ paoloricardo: ในทางกลับกันฉันคิดว่าการมี std :: แสดงทั่วสถานที่นั้นเป็นภาพที่ไม่จำเป็น
Michael Burr

1
@ ไมเคิล: คุณจ่ายเงินของคุณและคุณเลือกได้!
paoloricardo

2
ขอขอบคุณที่สละเวลาเพิ่มรายละเอียดของปัญหาที่คุณพบ
Michael Burr

4

หากคุณไม่มีความเสี่ยงที่ชื่อจะขัดแย้งกันในรหัสของคุณกับ std และไลบรารีอื่น ๆ คุณสามารถใช้ได้:

using namespace std;

แต่ถ้าคุณต้องการทราบการอ้างอิงรหัสของคุณสำหรับเอกสารประกอบอย่างแม่นยำหรือมีความเสี่ยงที่ชื่อจะขัดแย้งกันให้ใช้วิธีอื่น:

using std::string;
using std::cout;

วิธีที่สามอย่าใช้โซลูชันเหล่านี้และเขียน std :: ก่อนการใช้งานแต่ละครั้งในโค้ดจะทำให้คุณมีความปลอดภัยมากขึ้น แต่โค้ดอาจจะหนักกว่าเล็กน้อย ...


4

ทั้งสอง

using std::string;

และ

using namespace std;

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

ในการใช้งานไฟล์ (.cpp) ขึ้นอยู่กับคุณ (อย่าลืมทำตามคำสั่ง #include ทั้งหมดเท่านั้น) คุณสามารถแบ่งโค้ดได้เฉพาะในไฟล์เฉพาะนี้ดังนั้นจึงง่ายต่อการจัดการและค้นหาสาเหตุของความขัดแย้งของชื่อ หากคุณต้องการใช้ std :: (หรือคำนำหน้าอื่น ๆ อาจมีหลายเนมสเปซในโปรเจ็กต์ของคุณ) ก่อนตัวบ่งชี้ก็ใช้ได้ หากคุณต้องการเพิ่มตัวระบุที่คุณใช้กับเนมสเปซส่วนกลางก็ใช้ได้ หากคุณต้องการนำเนมสเปซทั้งหมดมาไว้บนหัวของคุณ :-) ขึ้นอยู่กับคุณ แม้ว่าเอฟเฟกต์จะ จำกัด ไว้ที่หน่วยคอมไพล์เดียว แต่ก็ยอมรับได้


3

สำหรับฉันฉันชอบใช้::เมื่อเป็นไปได้

std::list<int> iList;

ฉันเกลียดที่จะเขียน:

for(std::list<int>::iterator i = iList.begin(); i != iList.end(); i++)
{
    //
}

หวังว่าด้วย C ++ 0x ฉันจะเขียนสิ่งนี้:

for(auto i = iList.begin(); i != iList.end(); i++)
{
    //
}

หากเนมสเปซมีความยาวมาก

namespace dir = boost::filesystem;

dir::directory_iterator file("e:/boost");
dir::directory_iterator end;

for( ; file != end; file++)
{
    if(dir::is_directory(*file))
        std::cout << *file << std::endl;
}

@AraK: namespace dir = boost :: filesystem; ฉันเดาว่านี่คือนามแฝง?
paoloricardo

@ paoloricardo: ใช่ว่ามันคืออะไร
sbi

2
ตัวทำซ้ำควรจะเพิ่มขึ้นด้วย++iไม่ใช่i++เพราะหากกำหนดไว้ด้วยซ้ำจะสร้างสำเนาของตัววนซ้ำชั่วคราวที่ไม่จำเป็น
Felix Dombek

2

คุณไม่ควรอยู่using namespace stdในขอบเขตเนมสเปซในส่วนหัว นอกจากนี้ฉันคิดว่าโปรแกรมเมอร์ส่วนใหญ่จะสงสัยเมื่อเห็นvectorหรือstringไม่เห็นstd::ดังนั้นฉันคิดว่าไม่using namespace stdดีกว่า ดังนั้นฉันเถียงว่าไม่เคยเป็นusing namespace stdเลย

หากคุณรู้สึกว่าต้องเพิ่มในท้องถิ่นโดยใช้การประกาศเช่นusing std::vector. แต่ถามตัวเองว่าคุ้มไหม? โค้ดบรรทัดหนึ่งเขียนครั้งเดียว (อาจจะสองครั้ง) แต่อ่านสิบ, แสนหรือพันครั้ง ความพยายามในการพิมพ์ที่บันทึกไว้ในการเพิ่มคำประกาศหรือคำสั่งโดยใช้นั้นมีน้อยมากเมื่อเทียบกับความพยายามในการอ่านรหัส

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

ฉันพบว่าโดยปกติแล้วผู้ที่ต่อต้านการแบนusingมักจะไม่ได้ลองทำโครงการเดียว ผู้ที่ได้ลองใช้แล้วมักจะพบว่ามันดีกว่าการใช้คำสั่ง / การประกาศในเวลาอันสั้น

หมายเหตุ: ข้อยกเว้นเดียวusing std::swapคือสิ่งที่จำเป็น (โดยเฉพาะในโค้ดทั่วไป) ในการรับโอเวอร์โหลดswap()ที่ไม่สามารถใส่ลงในstdเนมสเปซได้ (เนื่องจากเราไม่ได้รับอนุญาตให้ใส่stdฟังก์ชันมากเกินไปในเนมสเปซนี้)


3
ความเชี่ยวชาญเฉพาะของ std :: swap จะเป็นความเชี่ยวชาญที่สมบูรณ์ - คุณไม่สามารถเชี่ยวชาญเทมเพลตฟังก์ชันบางส่วนได้ โปรแกรมใด ๆจะได้รับอนุญาตให้บางส่วนมีความเชี่ยวชาญแม่แบบห้องสมุดมาตรฐานใด ๆ ตราบใดที่ความเชี่ยวชาญที่ขึ้นอยู่กับประเภทที่ผู้ใช้กำหนด
CB Bailey

@ ชาร์ลส์: ใช่คุณพูดถูกแน่นอนไม่มี FTPS และฉันสามารถเชี่ยวชาญเทมเพลตได้ภายในstdแต่ไม่มากเกินไป ขออภัยที่สมองฝ่อ ฉันจะแก้ไขโพสต์
sbi

2
ฉันไม่คิดว่าเจตนาของusing namespaceคำสั่งคือการพิมพ์ ; แทนที่จะทำให้การอ่านง่ายขึ้นเพราะอย่างที่คุณพูดรหัสนั้นจะต้องอ่านหลายสิบหลายร้อยหรือหลายพันครั้ง และสำหรับบางคนก็อ่านง่ายขึ้นมากโดยไม่std::เกะกะ แต่นั่นอาจขึ้นอยู่กับความสามารถในการรับรู้ส่วนบุคคล บางคนกรองstd::ออกไปหรือแม้กระทั่งต้องการคำแนะนำ (เช่นเซริฟ) บางคนสะดุดและรู้สึกเหมือนอยู่บนถนนที่เป็นหลุมเป็นบ่อ
Lumi


1
@sbi: ไม่นั่นไม่ใช่วัตถุประสงค์ ขึ้นอยู่กับว่าคุณคิดว่า std :: มีประโยชน์หรือไม่เกะกะ ความยุ่งเหยิงมากขึ้น -> ความชัดเจนน้อยลง
Joshua Richardson

2

เนมสเปซเก็บรหัสไว้เพื่อป้องกันความสับสนและมลพิษของลายเซ็นฟังก์ชัน

นี่คือการสาธิตการ ใช้งานเนมสเปซที่เหมาะสมที่สมบูรณ์และเป็นเอกสาร:

#include <iostream>
#include <cmath>  // Uses ::log, which would be the log() here if it were not in a namespace, see /programming/11892976/why-is-my-log-in-the-std-namespace

// Silently overrides std::log
//double log(double d) { return 420; }

namespace uniquename {
    using namespace std;  // So we don't have to waste space on std:: when not needed.

    double log(double d) {
        return 42;
    }

    int main() {
        cout << "Our log: " << log(4.2) << endl;
        cout << "Standard log: " << std::log(4.2);
        return 0;
    }
}

// Global wrapper for our contained code.
int main() {
    return uniquename::main();
}

เอาท์พุต:

Our log: 42
Standard log: 1.43508

1

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

การระบุรายการที่คุณต้องการนำเข้าด้วยตนเองเพื่อstdป้องกันไม่ให้สิ่งนั้นเกิดขึ้น แต่อาจส่งผลให้มีรายการการใช้งานที่ยาวในตอนต้นของไฟล์ซึ่งนักพัฒนาบางคนจะมองว่าน่าเกลียด;)!

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

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

EDIT2: แก้ไขคำตอบของฉันขอบคุณความคิดเห็นของ Charles


2
using namespace std;นำเข้าเนื้อหาของstdเนมสเปซไปยังเนมสเปซส่วนกลาง จะไม่เปลี่ยนเนมสเปซเริ่มต้น การกำหนดบางสิ่งในเนมสเปซส่วนกลางหลังจากที่using namespace stdจะไม่ใส่ลงในstdเนมสเปซ
CB Bailey

ขออภัยนี่ไม่ใช่สิ่งที่ฉันหมายถึง ขอบคุณที่ชี้ให้ดูฉันจะแก้ไขคำตอบให้
Wookai

1
Guys: ขอบคุณสำหรับคำตอบ ดูเหมือนว่าโดยทั่วไปจะปลอดภัยกว่าที่จะไม่ใช้ "การใช้เนมสเปซ std" และหลีกเลี่ยงการสร้างความคลุมเครือที่อาจเกิดขึ้น ความสมดุลโดยใช้ 'std :: xxx' ดึงดูดฉันมากกว่าการประกาศรายการฟังก์ชั่นต่างๆที่จุดเริ่มต้นของไฟล์ต้นทางเนื่องจากมีคุณสมบัติที่ชัดเจนว่าเจตนาของคน ๆ หนึ่งคืออะไร
paoloricardo

1
ใบเสนอราคา (ยกเว้นเมื่อเนมสเปซยาวเกินไป) คุณสามารถใช้นามแฝงเนมสเปซเพื่อช่วยได้ 'namespace Rv1 = Thor :: XML :: XPath :: กฎ :: Light :: Version1;' สังเกตนามแฝงและใช้ทั้งสองตามกฎการกำหนดขอบเขต
Martin York

0

เช่นเดียวกับใน Java ที่คุณสามารถใช้ java.util * หรือเลือกแต่ละคลาสทีละคลาสก็ได้ขึ้นอยู่กับสไตล์ โปรดทราบว่าคุณไม่ต้องการหนึ่งusing namespace stdที่เริ่มต้นของขอบเขตไฟล์ / กว้างของคุณเพราะคุณจะก่อให้เกิดมลพิษ namespace และอาจจะมีการปะทะกันชนะจุด namespaces แต่ถ้าคุณมีฟังก์ชั่นที่ใช้ STL จำนวนมากมันจะถ่วงรหัสเพื่อให้มีไวยากรณ์คำนำหน้าในตรรกะของคุณและคุณควรพิจารณาใช้อย่างใดอย่างหนึ่งusing namespace std(เมื่อใช้คลาสต่างๆ) หรือแต่ละusings (เมื่อใช้ไม่กี่คลาสเรียนบ่อย)


0

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

นั่นเป็นเพราะสิ่งที่คุณต้องการให้รหัสของคุณดูเหมือนขึ้นอยู่กับงานในมือ

ในขณะที่สร้างซอร์สโค้ดของฉันฉันต้องการดูว่าฉันกำลังใช้คลาสใดอยู่: มันstd::stringหรือBuzFlox::Obs::stringคลาส?

เมื่อออกแบบโฟลว์การควบคุมฉันไม่ได้สนใจประเภทของตัวแปรด้วยซ้ำ แต่ฉันต้องการโฟกัสที่if's and while' s and continue's

นี่คือคำแนะนำของฉัน:

ขึ้นอยู่กับผู้ชมของโค้ดและพลังของเครื่องมือของคุณให้เลือกวิธีที่อ่านง่ายที่สุดหรือให้ข้อมูลส่วนใหญ่


0

มีหลายวิธีในการแก้ไข

ขั้นแรก: ใช้เหมือนกับสิ่งที่คุณทำ

ประการที่สอง: ทำnamespace S = std;ลด 2 ตัวอักษร

ที่สาม: staticการใช้งาน

ประการที่สี่: อย่าใช้ชื่อที่stdใช้


-1

ข้อดีข้อเสียของแต่ละข้อคืออะไร

เหตุผลเดียวที่จะละทิ้งมาตรฐาน :: ในทางทฤษฎีคุณสามารถใช้ฟังก์ชัน STL ทั้งหมดด้วยตัวเองได้ จากนั้นฟังก์ชันของคุณสามารถเปลี่ยนจากการใช้ std :: vector เป็น my :: vector ได้โดยไม่ต้องเปลี่ยนรหัส


Namespaces ไม่ได้ออกแบบมาเพื่อให้สามารถแทนที่ชื่อที่มีฟังก์ชันการทำงานที่แตกต่างกัน แต่เทียบเท่ากันได้ ได้รับการออกแบบมาเพื่อป้องกันการปะทะกันของชื่อโดยไม่ได้ตั้งใจ
Michael Burr

ใช่ดังนั้นเหตุผลเดียวสำหรับคำสั่ง 'การใช้' ที่ทำลายสิ่งนี้คือการอนุญาตให้คุณเปลี่ยนฟังก์ชันเป็นเนมสเปซใหม่
Martin Beckett

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

ฉันคิดว่าการ "ใช้" มีจุดประสงค์เพื่อให้คุณเปลี่ยนไปใช้การใช้งานทางเลือกแทนการบันทึกการพิมพ์ 3 ตัวอักษร ฉันชอบใช้ "std :: Foo" เพราะทำหน้าที่เป็นสัญญากับโปรแกรมเมอร์ว่าฉันใช้ Foo ปกติและพวกเขาไม่ต้องตรวจสอบ ฉันยอมรับว่าฉันไม่ต้องการพิมพ์ "com.microsoft.visual-studio.standard-library.numbers.int foo" การประกาศตัววนซ้ำบางรายการใน STL จะเป็นเช่นนี้ Python ทำได้ดีมากในการให้คุณดึงชุดฟังก์ชันที่ตกแต่งหรือไม่ได้รับการตกแต่งจากโมดูล
Martin Beckett

-1

ทำไมไม่ยกตัวอย่าง

typedef std::vector<int> ints_t;
ints_t ints1;
....
ints_t ints2;

แทนที่จะเทอะทะ

std::vector<int> ints1;
...
std::vector<int> ints2;

ฉันพบว่าอ่านได้ง่ายกว่ามากและเป็นมาตรฐานสำหรับการเข้ารหัสของฉัน

คุณยังสามารถใช้เพื่อรวมข้อมูลความหมายบางอย่างสำหรับผู้อ่าน ตัวอย่างเช่นพิจารณาต้นแบบฟังก์ชัน

void getHistorgram(std::vector<unsigned int>&, std::vector<unsigned int>&);

ค่าที่ส่งคืนคืออะไร?

วิธีการเกี่ยวกับแทน

typedef std::vector<unsigned int> values_t;
typedef std::vector<unsigned int> histogram_t;
...
void getHistogram(values_t&, histogram_t&); 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.