Logic đằng sau các từ khóa sử dụng từ khóa trong C ++ là gì?


145

Logic đằng sau từ khóa "sử dụng" trong C ++ là gì?

Nó được sử dụng trong các tình huống khác nhau và tôi đang cố gắng tìm xem liệu tất cả những thứ đó có điểm chung hay không và có một lý do tại sao từ khóa "sử dụng" được sử dụng như vậy.

using namespace std; // to import namespace in the current namespace
using T = int; // type alias
using SuperClass::X; // using super class methods in derived class

53
Commitee tiêu chuẩn ghét giới thiệu từ khóa mới vào ngữ pháp C ++.
Teh internets được làm bằng catz

4
@tehi INTERNetsismadeofcatz Nếu đó là logic thực sự, xin vui lòng cho tôi biết tôi sẽ tự sát ngay bây giờ.
dùng3111311

62
@ user3111311: Bạn có nhận ra ý nghĩa của việc giới thiệu các từ dành riêng mới, phải không? Điều đó có nghĩa là tất cả các mã hiện có sử dụng những mã này làm tên định danh đột nhiên không biên dịch được. Đó là một điều thú vị. Ví dụ: có rất nhiều mã C không thể biên dịch thành C ++ vì nó chứa những thứ như thế int class;. Sẽ còn tồi tệ hơn nữa nếu mã C ++ đột nhiên ngừng là C ++ hợp lệ.
Ben Voigt

7
@BenVoigt: Thực tế là mã C sử dụng int class;sẽ không được biên dịch vì C ++ không hoàn toàn là một điều xấu. Nó có thể được sử dụng để đảm bảo rằng mã C sẽ được biên dịch thành C. Thật dễ dàng để quên rằng C và C ++ là hai ngôn ngữ khác nhau - và thực tế, có mã đó là C hợp lệ và C ++ hợp lệ, nhưng với ngữ nghĩa khác nhau.
Keith Thompson

1
Về mặt đó, usingkhông tệ hơn (hoặc tốt hơn) hơn static. IMHO quan điểm không giới thiệu từ khóa mới là rất quan trọng như được giải thích bởi teh internets được làm từ catz và Ben Voigt.
Cassio Neri

Câu trả lời:


114

Trong C ++ 11, usingtừ khóa khi được sử dụng type aliaslà giống hệt với typedef.

7.1.3.2

Tên typedef cũng có thể được giới thiệu bằng một khai báo bí danh. Mã định danh theo sau từ khóa đang sử dụng trở thành tên typedef và thuộc tính-specifier-seq tùy chọn theo sau định danh xác định với tên typedef đó. Nó có cùng ngữ nghĩa như thể được giới thiệu bởi công cụ xác định typedef. Cụ thể, nó không định nghĩa một loại mới và nó sẽ không xuất hiện trong id loại.

Bjarne Stroustrup cung cấp một ví dụ thực tế:

typedef void (*PFD)(double);    // C style typedef to make `PFD` a pointer to a function returning void and accepting double
using PF = void (*)(double);    // `using`-based equivalent of the typedef above
using P = [](double)->void; // using plus suffix return type, syntax error
using P = auto(double)->void // Fixed thanks to DyP

Pre-C ++ 11, usingtừ khóa có thể đưa các chức năng thành viên vào phạm vi. Trong C ++ 11, bây giờ bạn có thể làm điều này cho các nhà xây dựng (một ví dụ khác về Bjarne Stroustrup):

class Derived : public Base { 
public: 
    using Base::f;    // lift Base's f into Derived's scope -- works in C++98
    void f(char);     // provide a new f 
    void f(int);      // prefer this f to Base::f(int) 

    using Base::Base; // lift Base constructors Derived's scope -- C++11 only
    Derived(char);    // provide a new constructor 
    Derived(int);     // prefer this constructor to Base::Base(int) 
    // ...
}; 

Ben Voight cung cấp một lý do khá chính đáng đằng sau lý do không giới thiệu từ khóa mới hoặc cú pháp mới. Các tiêu chuẩn muốn tránh phá vỡ mã cũ càng nhiều càng tốt. Đây là lý do tại sao trong các văn bản đề nghị, bạn sẽ thấy phần thích Impact on the Standard, Design decisionsvà làm thế nào họ có thể ảnh hưởng đến mã cũ. Có những tình huống khi một đề xuất có vẻ như là một ý tưởng thực sự tốt nhưng có thể không có lực kéo bởi vì nó quá khó để thực hiện, quá khó hiểu hoặc sẽ mâu thuẫn với mã cũ.


Đây là một bài báo cũ từ 2003 n1449 . Sự hợp lý dường như có liên quan đến các mẫu. Cảnh báo: có thể có lỗi chính tả do sao chép từ PDF.

Trước tiên hãy xem xét một ví dụ về đồ chơi:

template <typename T>
class MyAlloc {/*...*/};

template <typename T, class A>
class MyVector {/*...*/};

template <typename T>

struct Vec {
typedef MyVector<T, MyAlloc<T> > type;
};
Vec<int>::type p; // sample usage

Vấn đề cơ bản với thành ngữ này và thực tế thúc đẩy chính cho đề xuất này là thành ngữ này làm cho các tham số mẫu xuất hiện trong ngữ cảnh không thể khấu trừ. Đó là, sẽ không thể gọi hàm foo bên dưới mà không chỉ định rõ ràng các đối số khuôn mẫu.

template <typename T> void foo (Vec<T>::type&);

Vì vậy, cú pháp có phần xấu xí. Chúng tôi muốn tránh lồng nhau ::type Chúng tôi muốn một cái gì đó như sau:

template <typename T>
using Vec = MyVector<T, MyAlloc<T> >; //defined in section 2 below
Vec<int> p; // sample usage

Lưu ý rằng chúng tôi đặc biệt tránh thuật ngữ khuôn mẫu typedef trực tuyến, và sử dụng cú pháp mới liên quan đến cặp sử dụng bằng cách sử dụng và sử dụng để tránh nhầm lẫn: chúng tôi không định nghĩa bất kỳ loại nào ở đây, chúng tôi đang giới thiệu một từ đồng nghĩa một sự trừu tượng hóa của một loại id (tức là biểu thức kiểu) liên quan đến các tham số mẫu. Nếu các tham số mẫu được sử dụng trong các ngữ cảnh có thể khấu trừ trong biểu thức kiểu thì bất cứ khi nào bí danh mẫu được sử dụng để tạo id mẫu, các giá trị của các tham số mẫu tương ứng có thể được suy ra - nhiều hơn về điều này sẽ theo sau. Trong mọi trường hợp, giờ đây có thể viết các hàm chung hoạt động Vec<T>trong ngữ cảnh có thể khấu trừ và cú pháp cũng được cải thiện. Ví dụ, chúng ta có thể viết lại foo như:

template <typename T> void foo (Vec<T>&);

Chúng tôi nhấn mạnh ở đây rằng một trong những lý do chính để đề xuất bí danh mẫu là để suy luận đối số và lời kêu gọi foo(p) sẽ thành công.


Bài viết tiếp theo n1361 giải thích lý do tại sao usingthay vì sử dụng typedef:

Nó đã được đề xuất (tái) sử dụng từ khóa typedef - như được thực hiện trong bài báo [4] - để giới thiệu các bí danh mẫu:

template<class T> 
    typedef std::vector<T, MyAllocator<T> > Vec;

Ký hiệu đó có lợi thế là sử dụng một từ khóa đã biết để giới thiệu một bí danh loại. Tuy nhiên, nó cũng hiển thị một số nhược điểm trong đó có sự nhầm lẫn khi sử dụng từ khóa được biết để giới thiệu bí danh cho tên loại trong ngữ cảnh mà bí danh không chỉ định một loại, mà là một mẫu; Veckhông phải là bí danh cho một loại và không nên lấy tên typedef. Tên Vecnày là một tên cho gia đình std::vector< [bullet] , MyAllocator< [bullet] > > - trong đó viên đạn là một giữ chỗ cho một loại tên. Do đó, chúng tôi không đề xuất cú pháp typedef Lần. Mặt khác câu

template<class T>
    using Vec = std::vector<T, MyAllocator<T> >;

có thể được đọc / giải thích như sau: từ bây giờ, tôi sẽ sử dụng Vec<T>làm từ đồng nghĩa cho std::vector<T, MyAllocator<T> >. Với cách đọc đó, cú pháp mới cho bí danh có vẻ hợp lý hợp lý.

Tôi nghĩ rằng sự khác biệt quan trọng được thực hiện ở đây, bí danh thay vì loại s. Một trích dẫn khác từ cùng một tài liệu:

Một khai báo bí danh là một tuyên bố, và không phải là một định nghĩa. Một khai báo bí danh giới thiệu một tên vào vùng khai báo dưới dạng bí danh cho loại được chỉ định bởi phía bên phải của khai báo. Cốt lõi của đề xuất này liên quan đến các bí danh tên loại, nhưng ký hiệu rõ ràng có thể được khái quát hóa để cung cấp các cách viết thay thế của bí danh không gian tên hoặc đặt tên các hàm quá tải (xem ✁ 2.3 để thảo luận thêm). [ Lưu ý của tôi: Phần đó thảo luận về cú pháp đó có thể trông như thế nào và lý do tại sao nó không phải là một phần của đề xuất. ] Có thể lưu ý rằng khai báo bí danh sản xuất ngữ pháp được chấp nhận ở bất cứ nơi nào một tuyên bố typedef hoặc một định nghĩa bí danh không gian tên được chấp nhận.

Tóm tắt, cho vai trò của using:

  • bí danh mẫu (hoặc typedefs mẫu, tên cũ được ưu tiên đặt tên)
  • bí danh không gian tên (nghĩa là, namespace PO = boost::program_optionsusing PO = ... tương đương)
  • tài liệu nói A typedef declaration can be viewed as a special case of non-template alias-declaration . Đó là một thay đổi thẩm mỹ, và được coi là giống hệt nhau trong trường hợp này.
  • đưa một cái gì đó vào phạm vi (ví dụ, namespace stdvào phạm vi toàn cầu), các hàm thành viên, kế thừa các hàm tạo

không thể được sử dụng cho:

int i;
using r = i; // compile-error

Thay vào đó hãy làm:

using r = decltype(i);

Đặt tên cho một bộ quá tải.

// bring cos into scope
using std::cos;

// invalid syntax
using std::cos(double);

// not allowed, instead use Bjarne Stroustrup function pointer alias example
using test = std::cos(double);

2
@ user3111311 Bạn đã nghĩ đến từ khóa nào khác? "Tự động"? "Đăng ký"?
Raymond Chen

2
using P = [](double)->void;là, AFAIK, không hợp lệ C ++ 11. Tuy nhiên, đây là: using P = auto(double)->void;và tạo ra một loại chức năng (chẳng hạn như P*một con trỏ hàm).
dyp

2
Tên anh ta là Bjarne Stroustrup;) (lưu ý r thứ hai trong Stroustrup)
dyp 26/12/13

1
@RaymondChen: thực sự registersẽ không tệ đến thế, nằm trong:register X as Y
MFH 27/12/13

1
Thật không may, registerbắt đầu một khai báo biến vì vậy điều này đã có một ý nghĩa. Khai báo một biến đăng ký được gọi là Y loại X.
Raymond Chen
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.