2012-06-13 29 views
260

C++ 11 cho phép inline namespace s, tất cả các thành viên trong số đó cũng được tự động đính kèm theo số namespace. Tôi không thể nghĩ ra bất kỳ ứng dụng hữu ích nào của điều này - có thể ai đó xin vui lòng đưa ra một ví dụ ngắn gọn, ngắn gọn về một tình huống cần có inline namespace và đâu là giải pháp thành ngữ nhất?Các không gian tên nội tuyến là gì?

(Ngoài ra, nó không phải là rõ ràng với tôi những gì sẽ xảy ra khi một namespace được khai báo inline trong một nhưng không phải tất cả các tờ khai, có thể sống trong các tập tin khác nhau. Đó không phải là cầu xin cho các rắc rối?)

Trả lời

259

namespace Inline là một thư viện phiên bản tính năng tương tự như symbol versioning, nhưng thực hiện hoàn toàn ở cấp C++ 11 (tức. Cross-platform) thay vì là một tính năng của một định dạng thực thi nhị phân cụ thể (nền tảng cụ thể ví dụ.) . Đây là một cơ chế mà tác giả thư viện có thể tạo ra một không gian tên lồng nhau và hành động như thể tất cả các khai báo của nó nằm trong không gian tên xung quanh (các không gian tên nội tuyến có thể được lồng nhau, do đó, các tên "lồng nhau" sẽ tăng lên tất cả các cách đến không gian tên không nội tuyến đầu tiên và xem và hành động như thể các khai báo của chúng cũng nằm trong bất kỳ không gian tên nào ở giữa).

Ví dụ: xem xét việc triển khai STL của vector. Nếu chúng ta có không gian tên inline từ đầu C++, sau đó trong C++ 98 header <vector> có thể trông như thế này:

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 

Tùy thuộc vào giá trị của __cplusplus, một hoặc các vector thi khác được chọn. Nếu codebase của bạn được viết bằng tiền C++ 98 lần, và bạn thấy rằng phiên bản C++ 98 của vector gây rắc rối cho bạn khi bạn nâng cấp trình biên dịch, "tất cả" bạn phải làm là tìm các tham chiếu đến std::vector trong codebase của bạn và thay thế chúng bằng std::pre_cxx_1997::vector.

Hãy đến các tiêu chuẩn tiếp theo, và các nhà cung cấp STL chỉ lặp đi lặp lại các thủ tục một lần nữa, giới thiệu một không gian tên mới cho std::vector với emplace_back hỗ trợ (mà đòi hỏi C++ 11) và nội tuyến mà một khi và chỉ khi __cplusplus == 201103L.

OK, vậy tại sao tôi cần một tính năng ngôn ngữ mới cho tính năng này? Tôi có thể làm như sau để có cùng tác dụng, phải không?

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 

Tùy thuộc vào giá trị của __cplusplus, tôi nhận được một hoặc bên kia của hiện thực.

Và bạn sẽ gần như chính xác.

Hãy xem xét những điều sau C++ 98 mã người dùng hợp lệ (nó đã được phép chuyên đầy đủ mẫu mà sống trong không gian tên std trong C++ 98 đã được):

// 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 

này là hoàn toàn hợp lệ mã nơi người dùng cung cấp việc thực hiện của riêng mình một vectơ cho một tập hợp kiểu mà cô ấy dường như biết một cách thực hiện hiệu quả hơn so với cái được tìm thấy trong (bản sao của cô ấy) của STL.

Nhưng: Khi chuyên một mẫu, bạn cần phải làm như vậy trong không gian tên nó được khai báo trong Tiêu chuẩn nói rằng vector được khai báo trong namespace std, vì vậy đó là nơi người sử dụng chính đáng hy vọng sẽ chuyên các loại..

Mã này làm việc với một tổ chức phi phiên bản namespace std, hoặc với các tính năng không gian tên inline C++ 11, nhưng không phải với các trick phiên bản được sử dụng using namespace <nested>, bởi vì đó cho thấy nhiều chi tiết thực hiện mà không gian tên thật trong đó vector là được xác định không trực tiếp là std.

Có các lỗ khác mà bạn có thể phát hiện vùng tên lồng nhau (xem bình luận bên dưới), nhưng không gian tên nội tuyến cắm tất cả chúng. Và đó là tất cả để có nó. Vô cùng hữu ích cho tương lai, nhưng AFAIK Standard không quy định tên không gian tên nội tuyến cho thư viện chuẩn của riêng nó (mặc dù tôi đã được chứng minh là sai), vì vậy nó chỉ có thể được sử dụng cho các thư viện của bên thứ ba, chứ không phải bản thân tiêu chuẩn (trừ khi các nhà cung cấp trình biên dịch đồng ý về một lược đồ đặt tên).

+13

+1 để giải thích lý do tại sao 'sử dụng không gian tên V99;' không hoạt động trong ví dụ của Stroustrup. –

+2

Tôi cho rằng phiên bản trong thư viện chuẩn theo cách tiêu chuẩn, bị giới hạn so với phiên bản bên thứ 3.Nếu C++ 21 'vector' không tốt cho tôi, và tôi cần C++ 11' vectơ', thì điều đó có thể là do sự thay đổi đột ngột trong C++ 21. Nhưng có thể là do chi tiết triển khai mà tôi không nên dựa vào ngay từ đầu. Các tiêu chuẩn không thể yêu cầu rằng mỗi C + + 21 thực hiện cung cấp một 'std :: cxx_11 :: vector' đó là lỗi tương thích với bất kỳ quá khứ' std :: vector' từ cùng một nhà cung cấp. Thư viện của bên thứ ba có thể thực hiện điều đó, nếu họ nghĩ rằng nó đáng giá cho họ. –

+3

Và tương tự, nếu tôi bắt đầu một triển khai C++ 21 hoàn toàn mới từ đầu, thì tôi không muốn bị gánh nặng khi thực hiện rất nhiều điều vô nghĩa cũ trong 'std :: cxx_11'. Không phải mọi trình biên dịch sẽ luôn luôn thực hiện tất cả các phiên bản cũ của các thư viện chuẩn, mặc dù nó hấp dẫn vào lúc này để nghĩ rằng nó sẽ rất ít gánh nặng để yêu cầu hiện thực để lại trong cũ khi họ thêm mới, vì thực tế tất cả chúng đều là anyway. Tôi cho rằng tiêu chuẩn có thể được thực hiện một cách hữu ích là tùy chọn, nhưng với một tên chuẩn nếu có. –

52

http://www.stroustrup.com/C++11FAQ.html#inline-namespace (một tài liệu được viết và duy trì bởi Bjarne Stroustrup, người mà bạn nghĩ nên biết về hầu hết các động lực cho hầu hết các tính năng của C++ 11.)

Theo đó, nó cho phép phiên bản cho tương thích ngược. Bạn xác định nhiều không gian tên bên trong và tạo một không gian tên gần đây nhất inline. Hoặc dù sao, cái mặc định cho những người không quan tâm đến việc phiên bản. Tôi cho rằng phiên bản mới nhất có thể là phiên bản tương lai hoặc tiên tiến chưa được đặt mặc định.

Các ví dụ đưa ra là:

// 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 

Tôi không thấy ngay tại sao bạn không đặt using namespace V99; bên trong namespace Mine, nhưng tôi không phải hoàn toàn hiểu việc sử dụng hợp cụ thể để mất Lời của Bjarne cho nó về động cơ của ủy ban.

+0

Vì vậy, trên thực tế, phiên bản 'f (1)' cuối cùng sẽ được gọi từ không gian tên 'V99' nội tuyến? –

+1

@EitanT: vâng, vì không gian tên toàn cầu có 'sử dụng namespace Mine;' và không gian tên 'Mine' chứa tất cả mọi thứ từ không gian tên nội tuyến' Mine :: V99'. –

+0

nhưng để làm việc này, tôi sẽ cần thêm 'inline' vào tệp V99.h. Điều gì sẽ xảy ra khi tệp V100.h xuất hiện, khai báo 'không gian tên nội tuyến V100' và tôi cũng đưa nó vào trong' Mine'? 'F (int)' nào được gọi bởi 'f (1)'? – Walter

Các vấn đề liên quan