อินไลน์เนมสเปซคืออะไร


334

C ++ 11 ช่วยให้inline namespaces namespaceสมาชิกทั้งหมดที่มีโดยอัตโนมัติในการปิดล้อม ฉันไม่สามารถนึกถึงแอปพลิเคชั่นที่มีประโยชน์ของเรื่องนี้ - บางคนได้โปรดยกตัวอย่างสั้น ๆ รวบรัดของสถานการณ์ที่inline namespaceต้องการและที่เป็นวิธีแก้ปัญหาสำนวนที่สุด?

(นอกจากนี้ยังไม่ชัดเจนสำหรับฉันว่าจะเกิดอะไรขึ้นเมื่อ a namespaceถูกประกาศinlineในที่เดียว แต่ไม่ใช่การประกาศทั้งหมดซึ่งอาจอยู่ในไฟล์ที่แตกต่างกันนี่ไม่ใช่สิ่งที่ขอให้เกิดปัญหาหรือไม่)

คำตอบ:


339

Inline namespaces เป็นคุณลักษณะการกำหนดเวอร์ชันของไลบรารีคล้ายกับสัญลักษณ์การกำหนดเวอร์ชันแต่ดำเนินการอย่างหมดจดในระดับ C ++ 11 (เช่น. cross-platform) แทนที่จะเป็นคุณลักษณะของรูปแบบไบนารีที่ปฏิบัติการได้เฉพาะ (เช่นเฉพาะแพลตฟอร์ม)

มันเป็นกลไกที่ผู้เขียนห้องสมุดสามารถสร้างเนมสเปซที่ซ้อนกันและทำราวกับว่าการประกาศทั้งหมดของมันอยู่ในเนมสเปซที่ล้อมรอบ (อินสแตนซ์เนมสเปซสามารถซ้อนกันได้ดังนั้น - ใส่เนมสเปซแล้วมองและทำตัวราวกับประกาศในส่วนใดของเนมสเปซในระหว่างนั้นด้วย)

vectorเป็นตัวอย่างให้พิจารณาการดำเนินงานของ STL หากเรามีเนมสเปซแบบอินไลน์จากจุดเริ่มต้นของ C ++ ดังนั้นใน C ++ 98 ส่วนหัว<vector>อาจมีลักษณะเช่นนี้:

namespace std {

#if __cplusplus < 1997L // pre-standard C++
    inline
#endif

    namespace pre_cxx_1997 {
        template <class T> __vector_impl; // implementation class
        template <class T> // e.g. w/o allocator argument
        class vector : __vector_impl<T> { // private inheritance
            // ...
        };
    }
#if __cplusplus >= 1997L // C++98/03 or later
                         // (ifdef'ed out b/c it probably uses new language
                         // features that a pre-C++98 compiler would choke on)
#  if __cplusplus == 1997L // C++98/03
    inline
#  endif

    namespace cxx_1997 {

        // std::vector now has an allocator argument
        template <class T, class Alloc=std::allocator<T> >
        class vector : pre_cxx_1997::__vector_impl<T> { // the old impl is still good
            // ...
        };

        // and vector<bool> is special:
        template <class Alloc=std::allocator<bool> >
        class vector<bool> {
            // ...
        };

    };

#endif // C++98/03 or later

} // namespace std

เลือก__cplusplusอย่างใดอย่างหนึ่งหรือการvectorใช้งานอื่นขึ้นอยู่กับมูลค่าของ ถ้า codebase ของคุณถูกเขียนใน pre-C ++ 98 ครั้งและคุณจะพบว่า C ++ 98 รุ่นvectorเป็นสาเหตุของปัญหาสำหรับคุณเมื่อคุณอัพเกรดคอมไพเลอร์ของคุณ "ทุกคน" ที่คุณต้องทำคือการหาอ้างอิงถึงstd::vectorใน codebase std::pre_cxx_1997::vectorและแทนที่พวกเขาโดยคุณ

มามาตรฐานต่อไปและผู้ขาย STL เพียงแค่ทำซ้ำขั้นตอนอีกครั้งแนะนำ namespace ที่ใหม่สำหรับstd::vectorกับemplace_backการสนับสนุน (ซึ่งต้องใช้ภาษา C ++ 11) และอินไลน์ที่หนึ่ง __cplusplus == 201103LIFF

ตกลงดังนั้นทำไมฉันต้องมีคุณสมบัติภาษาใหม่สำหรับเรื่องนี้? ฉันสามารถทำสิ่งต่อไปนี้เพื่อให้มีผลเหมือนกันได้หรือไม่?

namespace std {

    namespace pre_cxx_1997 {
        // ...
    }
#if __cplusplus < 1997L // pre-standard C++
    using namespace pre_cxx_1997;
#endif

#if __cplusplus >= 1997L // C++98/03 or later
                         // (ifdef'ed out b/c it probably uses new language
                         // features that a pre-C++98 compiler would choke on)

    namespace cxx_1997 {
        // ...
    };
#  if __cplusplus == 1997L // C++98/03
    using namespace cxx_1997;
#  endif

#endif // C++98/03 or later

} // namespace std

ขึ้นอยู่กับมูลค่าของ__cplusplusฉันได้รับอย่างใดอย่างหนึ่งของการใช้งาน

และคุณเกือบจะถูกต้อง

พิจารณารหัสผู้ใช้ C ++ 98 ที่ถูกต้องต่อไปนี้ (ได้รับอนุญาตให้ใช้แม่แบบที่มีความเชี่ยวชาญอย่างเต็มรูปแบบที่อาศัยอยู่ในเนมสเปซstdใน C ++ 98 แล้ว):

// I don't trust my STL vendor to do this optimisation, so force these 
// specializations myself:
namespace std {
    template <>
    class vector<MyType> : my_special_vector<MyType> {
        // ...
    };
    template <>
    class vector<MyOtherType> : my_special_vector<MyOtherType> {
        // ...
    };
    // ...etc...
} // namespace std

นี่เป็นรหัสที่ถูกต้องสมบูรณ์แบบที่ผู้ใช้ระบุการใช้เวกเตอร์ของตัวเองสำหรับประเภทของชุดที่เห็นได้ชัดว่าเธอรู้ว่ามีการใช้งานที่มีประสิทธิภาพมากกว่าที่พบใน (สำเนาของเธอ) STL

แต่ : เมื่อเชี่ยวชาญเทมเพลตคุณต้องทำในเนมสเปซที่ถูกประกาศระบบ Standard บอกว่าvectorมีการประกาศในเนมสเปซstdดังนั้นนั่นคือสิ่งที่ผู้ใช้คาดหวังว่าจะมีความเชี่ยวชาญเฉพาะประเภท

รหัสนี้ทำงานร่วมกับ namespace ที่ไม่ใช่ versioned stdหรือซี ++ คุณลักษณะ namespace 11 แบบอินไลน์ แต่ไม่ได้มีเคล็ดลับเวอร์ชันที่ใช้using namespace <nested>เพราะ exposes ว่ารายละเอียดการดำเนินงานว่า namespace จริงที่vectorถูกกำหนดไม่ได้stdโดยตรง

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


23
+1 เพื่ออธิบายว่าทำไมusing namespace V99;ไม่ทำงานในตัวอย่างของ Stroustrup
Steve Jessop

3
และในทำนองเดียวกันถ้าฉันเริ่มต้นการใช้งาน C ++ 21 ใหม่ล่าสุดตั้งแต่เริ่มต้นฉันไม่ต้องการรับภาระเรื่องไร้สาระจำนวนมากstd::cxx_11มาใช้ คอมไพเลอร์ทุกคนจะใช้ไลบรารี่มาตรฐานรุ่นเก่าทุกรุ่นไม่ได้แม้จะคิดว่ามันน่าจะเป็นภาระน้อยมากที่จะต้องมีการติดตั้งแอพพลิเคชั่นที่มีอยู่ให้ทิ้งไว้เก่าเมื่อเพิ่มใหม่ อยู่แล้ว ฉันคิดว่าสิ่งที่มาตรฐานสามารถทำได้อย่างเป็นประโยชน์นั้นทำให้เป็นทางเลือก แต่มีชื่อมาตรฐานหากมี
Steve Jessop

46
นั่นไม่ใช่ทั้งหมดที่มีให้ ADL ก็เป็นอีกเหตุผลหนึ่ง (ADL จะไม่ติดตามการใช้คำสั่ง) และการค้นหาชื่อด้วย ( using namespace Aในเนมสเปซ B สร้างชื่อในเนมสเปซ B ซ่อนชื่อในเนมสเปซ A หากคุณมองหาB::name- ไม่ใช่เช่นนั้นด้วยอินสแตนซ์เนมสเปซ)
Johannes Schaub - litb

4
ทำไมไม่ใช้เพียงแค่การใช้ifdefเวกเตอร์แบบเต็ม? การใช้งานทั้งหมดจะอยู่ในเนมสเปซเดียว แต่จะมีเพียงหนึ่งการกำหนดหลังจากการประมวลผลล่วงหน้า
sasha.sochka

6
@ sasha.sochka เพราะในกรณีนี้คุณไม่สามารถใช้การใช้งานอื่น ๆ พวกเขาจะถูกลบออกโดยผู้ประมวลผลล่วงหน้า ด้วยอินไลน์เนมสเปซคุณสามารถใช้การดำเนินการใด ๆ ที่คุณต้องการโดยระบุชื่อที่ผ่านการรับรอง (หรือusingคำหลัก)
Vasily Biryukov

70

http://www.stroustrup.com/C++11FAQ.html#inline-namespace (เอกสารที่เขียนและดูแลโดย Bjarne Stroustrup ผู้ที่คุณคิดว่าควรตระหนักถึงแรงจูงใจส่วนใหญ่สำหรับคุณลักษณะ C ++ 11 ส่วนใหญ่ )

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

ตัวอย่างที่ให้มาคือ:

// file V99.h:
inline namespace V99 {
    void f(int);    // does something better than the V98 version
    void f(double); // new feature
    // ...
}

// file V98.h:
namespace V98 {
    void f(int);    // does something
    // ...
}

// file Mine.h:
namespace Mine {
#include "V99.h"
#include "V98.h"
}

#include "Mine.h"
using namespace Mine;
// ...
V98::f(1);  // old version
V99::f(1);  // new version
f(1);       // default version

ฉันไม่เห็นทันทีว่าทำไมคุณไม่ใส่using namespace V99;ในเนมสเปซMineแต่ฉันไม่จำเป็นต้องเข้าใจถึงกรณีการใช้งานทั้งหมดเพื่อที่จะนำคำพูดของ Bjarne มาใช้เป็นแรงจูงใจของคณะกรรมการ


ดังนั้นในความเป็นจริงf(1)รุ่นสุดท้ายจะถูกเรียกจากอินไลน์V99เนมสเปซ?
Eitan T

1
@EitanT: ใช่เพราะ namespace โลกมีusing namespace Mine;และMinenamespace มีทุกอย่างจาก Mine::V99namespace
Steve Jessop

2
@Walter: คุณลบออกinlineจากไฟล์V99.hในรุ่นที่มีV100.hอยู่ นอกจากนี้คุณยังแก้ไขMine.hในเวลาเดียวกันเพื่อเพิ่มการรวมพิเศษ Mine.hเป็นส่วนหนึ่งของไลบรารีไม่ใช่ส่วนหนึ่งของรหัสลูกค้า
Steve Jessop

5
@Walter: พวกเขาไม่ได้ติดตั้งV100.hพวกเขากำลังติดตั้งไลบรารีชื่อ "Mine" มีไฟล์ส่วนหัว 3 ไฟล์ใน "Mine" - Mine.h, V98.hและV99.h. มี 4 ไฟล์ส่วนหัวในรุ่น 100 ของ "ฉัน" เป็น - Mine.h, V98.h, และV99.h V100.hการจัดเรียงของไฟล์ส่วนหัวเป็นรายละเอียดการใช้งานที่ไม่เกี่ยวข้องกับผู้ใช้ หากพวกเขาค้นพบปัญหาความเข้ากันได้บางอย่างซึ่งหมายความว่าพวกเขาจำเป็นต้องใช้เฉพาะMine::V98::fจากรหัสบางส่วนหรือทั้งหมดพวกเขาสามารถผสมการโทรMine::V98::fจากรหัสเก่ากับการโทรไปยังMine::fในรหัสที่เขียนใหม่
Steve Jessop

2
@Walter ตามที่ระบุไว้ในคำตอบอื่น ๆ เทมเพลตจะต้องมีความเชี่ยวชาญในเนมสเปซที่พวกเขาประกาศไม่ใช่เนมสเปซที่ใช้ที่พวกเขาประกาศในขณะที่มันดูแปลก ๆMineแทนที่จะมีความเชี่ยวชาญในหรือMine::V99 Mine::V98
Justin Time - Reinstate Monica

8

นอกจากคำตอบอื่น ๆ ทั้งหมด

Inline namespace สามารถใช้เพื่อเข้ารหัสข้อมูล ABI หรือเวอร์ชั่นของฟังก์ชั่นในสัญลักษณ์ มันเป็นเพราะเหตุนี้พวกเขาจะใช้เพื่อให้เข้ากันได้ ABI ย้อนหลัง อินไลน์เนมสเปซช่วยให้คุณสามารถฉีดข้อมูลลงในชื่อ mangled (ABI) โดยไม่ต้องเปลี่ยนแปลง API เพราะจะมีผลกับชื่อสัญลักษณ์ของลิงเกอร์เท่านั้น

ลองพิจารณาตัวอย่างนี้:

สมมติว่าคุณเขียนฟังก์ชั่นFooที่อ้างอิงถึงวัตถุพูดbarและส่งคืนอะไร

พูดใน main.cpp

struct bar;
void Foo(bar& ref);

หากคุณตรวจสอบชื่อสัญลักษณ์ของคุณสำหรับไฟล์นี้หลังจากรวบรวมเป็นวัตถุ

$ nm main.o
T__ Z1fooRK6bar 

ชื่อสัญลักษณ์ linker อาจแตกต่างกัน แต่มันจะเข้ารหัสชื่อของฟังก์ชั่นและประเภทอาร์กิวเมนต์ที่ใดที่หนึ่ง

ตอนนี้อาจเป็นไปได้ว่าbarถูกกำหนดเป็น:

struct bar{
   int x;
#ifndef NDEBUG
   int y;
#endif
};

ขึ้นอยู่กับชนิดของการสร้างbarสามารถอ้างถึงสองประเภท / เค้าโครงที่แตกต่างกันด้วยสัญลักษณ์ลิงเกอร์เดียวกัน

เพื่อป้องกันพฤติกรรมดังกล่าวเราได้วางโครงสร้างของเราbarลงในเนมสเปซแบบอินไลน์โดยขึ้นอยู่กับชนิดของการสร้างสัญลักษณ์ของลิงเกอร์barจะแตกต่างกัน

ดังนั้นเราสามารถเขียน:

#ifndef NDEBUG
inline namespace rel { 
#else
inline namespace dbg {
#endif
struct bar{
   int x;
#ifndef NDEBUG
   int y;
#endif
};
}

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

$ nm rel.o
T__ ZROKfoo9relEbar
$ nm dbg.o
T__ ZROKfoo9dbgEbar

ชื่อสัญลักษณ์ Linker อาจแตกต่างกัน

สังเกตเห็นการปรากฏตัวของrelและdbgในชื่อสัญลักษณ์

ตอนนี้ถ้าคุณพยายามที่จะเชื่อมโยงการแก้ปัญหาด้วยโหมดการเปิดตัวหรือในทางกลับกันคุณจะได้รับข้อผิดพลาด linker ตรงกันข้ามกับข้อผิดพลาด runtime


1
ใช่มันสมเหตุสมผลแล้ว ดังนั้นนี่คือเพิ่มเติมสำหรับผู้ใช้งานไลบรารีและสิ่งที่คล้ายกัน
Walter

3

จริง ๆ แล้วฉันค้นพบการใช้อื่นสำหรับเนมสเปซแบบอินไลน์

ด้วยQtคุณจะได้รับพิเศษบางอย่างที่ดีมีใช้Q_ENUM_NSซึ่งจะต้องว่า namespace ล้อมรอบมี Q_NAMESPACEmeta-วัตถุซึ่งเป็นประกาศด้วย อย่างไรก็ตามเพื่อให้Q_ENUM_NSสามารถใช้งานได้จะต้องมีQ_NAMESPACE ไฟล์ที่เกี่ยวข้องในไฟล์เดียวกัน ⁽¹⁾ และสามารถมีได้เพียงหนึ่งรายการเท่านั้นหรือคุณได้รับข้อผิดพลาดคำจำกัดความที่ซ้ำกัน สิ่งนี้มีประสิทธิภาพหมายความว่าการแจกแจงทั้งหมดของคุณจะต้องอยู่ในส่วนหัวเดียวกัน yuck

หรือ ...คุณสามารถใช้เนมสเปซแบบอินไลน์ การซ่อนการแจกแจงในinline namespaceสาเหตุเมตาวัตถุจะมีชื่อที่แตกต่างกันในขณะที่ค้นหาผู้ใช้เช่นไม่มีเนมสเปซเพิ่มเติม⁽²⁾

ดังนั้นสิ่งเหล่านี้มีประโยชน์สำหรับการแบ่งสิ่งต่าง ๆ ออกเป็นหลาย ๆ ชื่อย่อยที่ทุกคนมีลักษณะเหมือนเนมสเปซเดียวหากคุณจำเป็นต้องทำเช่นนั้นด้วยเหตุผลบางประการ ของหลักสูตรนี้จะคล้ายกับการเขียนusing namespace innerใน namespace ด้านนอก แต่ไม่แห้งละเมิดของการเขียนชื่อของ namespace ภายในสองครั้ง


  1. มันแย่กว่านั้นจริงๆ มันจะต้องอยู่ในวงเล็บปีกกาเดียวกัน

  2. นอกจากว่าคุณพยายามเข้าถึง meta-object โดยที่ไม่ผ่านการรับรองทั้งหมด แต่ meta-object นั้นแทบจะไม่เคยใช้โดยตรง


คุณสามารถวาดภาพนั้นด้วยโครงกระดูกของรหัสได้หรือไม่? (นึกคิดโดยไม่มีการอ้างอิงถึง Qt อย่างชัดเจน) ทุกอย่างฟังดูค่อนข้างเกี่ยวข้อง / ไม่ชัดเจน
วอลเตอร์

ไม่ ... อย่างง่ายดาย เหตุผลที่ต้องใช้เนมสเปซแยกต่างหากเกี่ยวข้องกับรายละเอียดการใช้ Qt TBH มันยากที่จะจินตนาการถึงสถานการณ์นอก Qt ที่จะมีข้อกำหนดเดียวกัน อย่างไรก็ตามสำหรับสถานการณ์เฉพาะของ Qt นี้พวกเขามีประโยชน์มากมาย! ดูgist.github.com/mwoehlke-kitware/...หรือgithub.com/Kitware/seal-tk/pull/45เช่น
แมทธิว

0

ดังนั้นเพื่อสรุปประเด็นหลักusing namespace v99และinline namespaceไม่เหมือนเดิมคือวิธีแก้ปัญหาไลบรารีเวอร์ชันก่อนคำสำคัญเฉพาะ (inline) ได้รับการแนะนำใน C ++ 11 ซึ่งแก้ไขปัญหาการใช้usingงานในขณะที่มีฟังก์ชั่นการกำหนดเวอร์ชันเดียวกัน การusing namespaceใช้เพื่อทำให้เกิดปัญหากับ ADL (แม้ว่าตอนนี้ ADL จะปรากฏตามusingคำสั่ง) และความเชี่ยวชาญนอกชั้นเรียนของไลบรารีคลาส / ฟังก์ชั่น ฯลฯ โดยผู้ใช้จะไม่ทำงานถ้าทำนอกเนมสเปซจริง ผู้ใช้จะไม่ทราบและไม่ควรทราบนั่นคือผู้ใช้จะต้องใช้ B :: abi_v2 :: ไม่ใช่แค่ B :: สำหรับความเชี่ยวชาญในการแก้ไข)

//library code
namespace B { //library name the user knows
    namespace A { //ABI version the user doesn't know about
        template<class T> class myclass{int a;};
    }
    using namespace A; //pre inline-namespace versioning trick
} 

// user code
namespace B { //user thinks the library uses this namespace
    template<> class myclass<int> {};
}

first declaration of class template specialization of 'myclass' outside namespace 'A' is a C++11 extension [-Wc++11-extensions]นี้จะแสดงคำเตือนวิเคราะห์แบบคงที่ แต่ถ้าคุณสร้างเนมสเปซ A แบบอินไลน์คอมไพเลอร์จะแก้ไขความเชี่ยวชาญได้อย่างถูกต้อง แม้ว่าด้วยส่วนขยาย C ++ 11 ปัญหาจะหายไป

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

#include <iostream>
namespace A {
    namespace B{
        int a;
        int func(int a);
        template<class T> class myclass{int a;};
        class C;
        extern int d;
    } 
    using namespace B;
} 
int A::d = 3; //No member named 'd' in namespace A
class A::C {int a;}; //no class named 'C' in namespace 'A' 
template<> class A::myclass<int> {}; // works; specialisation is not an out-of-line definition of a declaration
int A::func(int a){return a;}; //out-of-line definition of 'func' does not match any declaration in namespace 'A'
namespace A { int func(int a){return a;};} //works
int main() {
    A::a =1; // works; not an out-of-line definition
}

ปัญหาหายไปเมื่อทำการ B แบบอินไลน์

ฟังก์ชั่นอื่น ๆinlineเนมสเปซได้อนุญาตให้ผู้เขียนไลบรารีจัดเตรียมการอัพเดตแบบโปร่งใสไปยังไลบรารี 1) โดยไม่บังคับให้ผู้ใช้รหัส refactor ด้วยชื่อเนมสเปซใหม่และ 2) การป้องกันการขาดรายละเอียดและ 3) ในขณะที่ 4) ให้การวินิจฉัย linker ที่เป็นประโยชน์และพฤติกรรมเดียวกันกับที่ใช้เนมสเปซที่ไม่ใช่อินไลน์ สมมติว่าคุณกำลังใช้ห้องสมุด:

namespace library {
    inline namespace abi_v1 {
        class foo {
        } 
    }
}

อนุญาตให้ผู้ใช้โทรออกlibrary::fooโดยไม่จำเป็นต้องทราบหรือรวมเวอร์ชัน ABI ในเอกสารซึ่งดูสะอาดตา การใช้library::abiverison129389123::fooจะดูสกปรก

เมื่อมีการอัพเดทคือการfooเพิ่มสมาชิกใหม่ให้กับคลาสมันจะไม่ส่งผลกระทบต่อโปรแกรมที่มีอยู่ในระดับ API เพราะพวกเขาไม่ได้ใช้สมาชิกอยู่แล้วและการเปลี่ยนแปลงชื่ออินไลน์เนมสเปซจะไม่เปลี่ยนแปลงอะไรเลยในระดับ API เพราะlibrary::fooจะยังคงทำงาน

namespace library {
    inline namespace abi_v2 {
        class foo {
            //new member
        } 
    }
}

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

ในสถานการณ์นี้:

namespace library {
    namespace abi_v1 {
        class foo {
        } 
    }

    inline namespace abi_v2 {
        class foo {
            //new member
        } 
    }
}

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

การใช้using namespaceมีฟังก์ชั่นน้อยกว่าการใช้inline(ในคำจำกัดความที่ไม่ได้กำหนดบรรทัด) แต่ให้ข้อดี 4 ประการเดียวกันกับด้านบน แต่คำถามที่แท้จริงคือทำไมต้องใช้วิธีแก้ปัญหาต่อไปเมื่อมีคำหลักเฉพาะที่ต้องทำ มันเป็นการปฏิบัติที่ดีกว่า verbose น้อยลง (ต้องเปลี่ยนรหัส 1 บรรทัดแทน 2) และทำให้ความตั้งใจชัดเจน

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