Sử dụng `using` trong C ++ hay tránh nó?


17

Giảm giá một cách tinh tế các ngữ nghĩa khác nhau do ADL, tôi thường nên sử dụng như thế nào usingvà tại sao? Có phụ thuộc vào tình huống không (ví dụ: tiêu đề sẽ là #included so với tệp nguồn sẽ không)?

Ngoài ra, tôi nên thích ::std::hay std::?

  1. Cấp độ không gian using namespace:

    using namespace std;
    
    pair<string::const_iterator, string::const_iterator>
    f(const string &s) {
        return make_pair(s.begin(), s.end());
    }
  2. Hoàn toàn rõ ràng:

    std::pair<std::string::const_iterator, std::string::const_iterator>
    f(const std::string &s) {
        return std::make_pair(s.begin(), s.end());
    }
  3. Khai báo cấp độ không gian tên:

    using std::pair;
    using std::string;
    
    pair<string::const_iterator, string::const_iterator>
    f(const string &s) {
        return make_pair(s.begin(), s.end());
    }
  4. Khai báo chức năng-cục bộ:

    std::pair<std::string::const_iterator, std::string::const_iterator>
    f(const std::string &s) {
        using std::make_pair;
        return make_pair(s.begin(), s.end());
    }
  5. Chức năng cục bộ using namespace:

    std::pair<std::string::const_iterator, std::string::const_iterator>
    f(const std::string &s) {
        using namespace std;
        return make_pair(s.begin(), s.end());
    }
  6. Thứ gì khác?

Đây là giả định tiền C ++ 14, và do đó không sử dụng loại trừ trả về auto.



@AProgrammer: Ah, cảm ơn vì liên kết, câu trả lời đó là một phần câu hỏi của tôi. :) Vẫn còn băn khoăn về ::std::so với std::mặc dù.
Mehrdad

4
Tôi đang sử dụng stdmà không có thứ hai mặc dù. Ai đó xác định một không gian tên std đang yêu cầu sự cố (và có thể tìm kiếm để tận dụng lợi thế mà hầu hết mọi người đang sử dụng stdvà không ::std).
AProgrammer

Câu trả lời:


25

Tránh sử dụng usingtrong các tiêu đề, vì điều đó phá vỡ mục đích của không gian tên.

Bạn có thể sử dụng nó trong các tệp nguồn, nhưng tôi vẫn sẽ tránh nó trong một số trường hợp (ví dụ using std).

Tuy nhiên, nếu bạn có các không gian tên lồng nhau, sẽ ổn thôi:

namespace A {
namespace B {
namespace C {
class s;
} // C
} // B
namespace D{
using B::C::s;
} // D
} // A

6
+1 Thật đáng ngạc nhiên khi có bao nhiêu hướng dẫn và các khóa học đại học chỉ cho bạn sử dụng usingtừ khóa mà không cần giải thích kỹ về lý do tại sao không gian tên được sử dụng để bắt đầu.
Jeffrey Sweeney

Mọi người muốn tiếp tục với việc sử dụng iostreams, chuỗi, v.v. Họ không muốn phải nhập std :: mỗi lần họ muốn sử dụng một thứ hoặc phải nhớ một phần khác của bản tóm tắt để đặt trước mã của họ, điều này sẽ gây ra các lỗi ít hữu ích hơn nếu họ quên nó . :(
Colen

Sẽ một cái gì đó như typedef std :: chuỗi s chuỗi; là một sự thay thế?
Giorgio

1
@Colen: Những linh hồn đáng thương này có thể sử dụng using std::coutvà bạn bè, nhưng nó không giống như coutmột cái tên dài khủng khiếp.
Benjamin Bannier

1
Nếu bạn là sinh viên đại học trong ngày đầu tiên tham gia "lớp C ++ đầu tiên của tôi", thì đó là một điều khác có thể gây ra lỗi cú pháp mà bạn không hiểu. Thật dễ dàng để chúng tôi tìm ra bởi vì chúng tôi là những lập trình viên giàu kinh nghiệm, nhưng khi bạn đang cố gắng học ngôn ngữ, đó là một điều phải lo lắng về việc bạn không cần.
Colen

11

Khi đặt một câu lệnh sử dụng trong một tệp nguồn, XIN VUI LÒNG, chỉ cần lấy những thứ bạn cần. Ví dụ:

using std::string;
using std::ostringstream;

Vấn đề ở đây là nếu bạn làm

using namespace std;

bạn kéo vào MỌI THỨ ĐỀU từ std vào không gian tên toàn cầu. Điều này dẫn đến các thông báo lỗi rất thú vị khi bạn vô tình sử dụng tên trong mã khớp với tên mà bạn hoàn toàn không biết trong std. Nếu bạn chỉ cần lấy những thứ bạn muốn, thì bạn sẽ không gặp phải vấn đề đó (hay chính xác hơn là lập trình viên tiếp theo làm việc với mã của bạn sẽ không gặp vấn đề đó).


Ngoài ra, bạn có thể using namespacechỉ trong một phạm vi chức năng, tránh vấn đề.
Tamás Szelei

2
@fish - thực sự, thực hiện 'sử dụng không gian tên' trong phạm vi chức năng không tránh được vấn đề, nó chỉ giới hạn không gian nơi mọi thứ có thể sai. Và nếu bạn kết thúc việc đặt 'sử dụng không gian tên' trong mọi chức năng, thì nó không khác nhiều so với việc thực hiện nó trên toàn cầu.
Michael Kohne

Mặc dù C ++ thực sự cho phép một người khai báo các loại ở cấp độ chức năng, nhưng nó không phải là một điều phổ biến; ngoài ra, các xung đột tên có thể dễ dàng phát hiện ra từ đầu ra của trình biên dịch (nhưng bạn đúng rằng điều này không ngăn cản chúng).
Tamás Szelei

2

Như VJovic chỉ ra, không sử dụng usingtrong tệp tiêu đề. usingtrong tệp tiêu đề ảnh hưởng đến đơn vị biên dịch hiện tại (tệp .cpp) theo cách mà tệp nguồn có thể không mong đợi.

using namespacecũng là để tránh trong một tập tin nguồn. Điều này mang mọi biểu tượng vào cùng một phạm vi như tệp nguồn. Nó rõ ràng hơn cho độc giả của bạn những gì bạn đang làm nếu bạn sử dụng các biểu tượng cụ thể từ không gian tên.


2
Là một mối quan tâm thực tế, trừ khi mã của bạn ghi đè các tên thường được sử dụng, tôi muốn thấy nhiều hơn using namespace JoystickModuleở phần đầu của tệp .cpp hơn là được JoystickModule::gắn vào mọi đối tượng trong suốt.
Alex P

@AlexP: Đó chính xác là cách tôi làm. Một usingtuyên bố cho không gian tên của riêng tôi hiện đang làm việc và mọi thứ khác vẫn được đặt tên.
Benjamin Kloster

Tôi nên giải thích về "sử dụng các ký hiệu cụ thể từ không gian tên". Thay vì tiền tố mỗi ký hiệu trên mỗi lần sử dụng, điều này không giúp ích cho khả năng đọc, tôi thích sử dụng cẩu biểu tượng rõ ràng. using SomeNameSpace::SomeSymbol. Điều này tránh di chuyển mọi biểu tượng từ không gian tên vào phạm vi hiện tại.
Cửa Bill

0

Viết usingtrong Headers là cách tốt nhất để tạo ra tất cả các loại lỗi khó chịukhông thể gỡ lỗi. Đừng không làm điều này.

Viết using namespace XYZtrong tệp nguồn tốt hơn một chút, nhưng vẫn có thể khiến bạn đau đầu vô số. Cách an toàn là chỉ định rõ ràng những gì bạn đang sử dụng, vd using Foo::Bar.

Giả sử bạn có Bar.cpp với các mục sau:

//Bar.cpp
using namespace Foo;
namespace
{
    double increment(double v) { return (v + 1); }
}

void Bar::someFunction()
{
    //...
    int currentIndex = 0;
    int nextIndex = increment(currentIndex);
    //...
}

Hàm này hoạt động tốt, cho đến một ngày - dường như không có bất kỳ thay đổi mã nào trong các lớp có liên quan - hành vi của nó thay đổi: Đột nhiên currentIndexdường như luôn bị tắt bởi một . Đi sâu vào những thay đổi gần đây bạn phát hiện ra không có thay đổi nào thậm chí liên quan đến mã từ xa.

Cuối cùng, bạn phát hiện ra nguyên nhân:
Bạn (gián tiếp) bao gồm Foo.hmột nơi nào đó. Trong các tệp cho không gian tên Foo, một chức năng mới đã được thêm vào:

//Foo.h
namespace Foo
{
    //...
    int& increment(int& v) { v += 1; return v; };
    //...
}

Đó là một trận đấu tốt hơn cho increment(int) so với chức năng của bạn increment(double)- vì vậy bây giờ Foo::increment()chức năng được gọi bằng Bar::someFunction()thay thế. Rất tiếc.

(Và nếu bạn định viết using trong Tiêu đề using namespace Foorất có thể là bất cứ nơi nào trong cây bao gồm của bạn ...)

Vì vậy, ... Đừng viết bất kỳ usingtiêu đề nào và cũng nên cảnh giác khi viết using namespacevào các tệp nguồn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.