C ++ dectype và dấu ngoặc đơn - tại sao?


32

Các chủ đề đã được thảo luận trước đây , nhưng đây không phải là một bản sao.

Khi ai đó hỏi về sự khác biệt giữa decltype(a)decltype((a)), câu trả lời thông thường là - alà một biến, (a)là một biểu thức. Tôi thấy câu trả lời này không thỏa mãn.

Đầu tiên, alà một biểu hiện là tốt. Các tùy chọn cho một biểu thức chính bao gồm, trong số những thứ khác -

  • ( biểu hiện )
  • biểu thức id

Quan trọng hơn, cụm từ cho dectype xem xét dấu ngoặc đơn rất, rất rõ ràng :

For an expression e, the type denoted by decltype(e) is defined as follows:
(1.1)  if e is an unparenthesized id-expression naming a structured binding, ...
(1.2)  otherwise, if e is an unparenthesized id-expression naming a non-type template-parameter, ...
(1.3)  otherwise, if e is an unparenthesized id-expression or an unparenthesized class member access, ...
(1.4)  otherwise, ...

Vì vậy, câu hỏi vẫn còn. Tại sao dấu ngoặc đơn được xử lý khác nhau? Có ai quen thuộc với các bài viết kỹ thuật hoặc các cuộc thảo luận của ủy ban đằng sau nó? Việc xem xét rõ ràng cho dấu ngoặc đơn dẫn đến việc nghĩ rằng đây không phải là một sự giám sát, do đó phải có một lý do kỹ thuật mà tôi thiếu.


2
"Câu trả lời thông thường là - a là một biến, (a) là một biểu thức" Ý nghĩa của chúng là " (a)là một biểu thức, và alà một biểu thức một biến".
HolyBlackCat

Câu trả lời:


18

Đó không phải là một sự giám sát. Thật thú vị, trong Decltype và auto (phiên bản 4) (N1705 = 04-0145) có một tuyên bố:

Các quy tắc dectype bây giờ nói rõ rằng decltype((e)) == decltype(e)(như được đề xuất bởi EWG).

Nhưng trong Decltype (phiên bản 6): từ ngữ được đề xuất (N2115 = 06-018) một trong những thay đổi là

Biểu thức ngoặc đơn bên trong dectype không được coi là một id-expression.

Không có lý do trong từ ngữ, nhưng tôi cho rằng đây là loại mở rộng của dectype sử dụng một cú pháp khác nhau, nói cách khác, nó nhằm mục đích phân biệt các trường hợp này.

Việc sử dụng cho điều đó được thể hiện trong dự thảo C ++ 9.8.8.4:

const int&& foo();
int i;
struct A { double x; };
const A* a = new A();
decltype(foo()) x1 = 17;        // type is const int&&
decltype(i) x2;                 // type is int
decltype(a->x) x3;              // type is double
decltype((a->x)) x4 = x3;       // type is const double&

Điều thực sự thú vị, là cách nó hoạt động với returntuyên bố:

decltype(auto) f()
{
    int i{ 0 };
    return (i);
}

Visual Studio 2019 của tôi đề nghị tôi loại bỏ dấu ngoặc thừa, nhưng thực tế chúng biến thành decltype((i))giá trị trả về int&làm cho nó trở thành UB kể từ khi trả về tham chiếu cho biến cục bộ.


Cảm ơn bạn đã chỉ ra nguồn gốc! Không chỉ dectype có thể được chỉ định khác nhau, hóa ra ban đầu nó là. Tài liệu rev-6 bổ sung rõ ràng hành vi dấu ngoặc đơn buồn cười này, nhưng bỏ qua lý do :(. Tôi đoán đó là gần với một câu trả lời như chúng ta sẽ nhận được bây giờ ..
Ofek Shilon

Nhưng chưa ai đề xuất để giải quyết vấn đề ngớ ngẩn bằng cách biến nó thành hai từ khóa riêng biệt, cho hai chức năng riêng biệt? Một trong những sai lầm lớn nhất của ủy ban (trong lịch sử đã tạo ra rất nhiều lỗi không được thực thi).
tò mò

13

Tại sao dấu ngoặc đơn được xử lý khác nhau?

Dấu ngoặc đơn không được đối xử khác nhau. Đó là biểu thức id không được gắn kết được xử lý khác nhau.

Khi dấu ngoặc đơn có mặt thì các quy tắc thông thường cho tất cả các biểu thức được áp dụng. Loại và giá trị loại được trích xuất và mã hóa trong loại decltype.

Điều khoản đặc biệt là có để chúng ta có thể viết mã hữu ích dễ dàng hơn. Khi áp dụng decltypetên của biến (thành viên), chúng ta thường không muốn một số loại đại diện cho các thuộc tính của biến khi được coi là biểu thức. Thay vào đó, chúng tôi chỉ muốn loại biến được khai báo, mà không phải áp dụng một tấn các đặc điểm loại để có được nó. Và đó chính xác decltypelà những gì được chỉ định để cung cấp cho chúng tôi.

Nếu chúng ta quan tâm đến các thuộc tính của biến như một biểu thức, thì chúng ta vẫn có thể nhận được nó khá dễ dàng, với một cặp dấu ngoặc đơn bổ sung.


Vì vậy, đối với một intthành viên icủa a, decltype(a.i)inttrong khi decltype((a.i))int&(giả sử akhông const)? Vì biểu thức a.iđược gán?
n314159

1
@ n314159 - Đó là ý chính của nó. Biểu thức a.ilà một giá trị không phải const, vì vậy bạn nhận được một loại tham chiếu lvalue không const cho (a.i).
Người kể chuyện - Unslander Monica

Tại sao 'các quy tắc thông thường cho tất cả các biểu thức' chỉ ra rằng loại của chúng là một tham chiếu? Tại sao 'các thuộc tính của biến dưới dạng biểu thức' bao gồm thuộc tính là tham chiếu? Có phải một số mặc định tự nhiên?
Ofek Shilon

1
@OfekShilon - Loại của họ không phải là tài liệu tham khảo. Loại biểu thức không bao giờ được phân tích như một tài liệu tham khảo . Nhưng dectlype chỉ có thể phân giải thành một loại và nó có nghĩa là cho chúng ta biết không chỉ là loại biểu thức, mà cả loại giá trị của nó. Danh mục giá trị được mã hóa bởi các loại tham chiếu. giá trị là &, xvalues ​​là &&và giá trị không phải là loại tham chiếu.
Người kể chuyện - Unslander Monica

@StoryTeller chính xác, không có sự khác biệt 'tự nhiên' giữa các loại biểu thức và không biểu thức. Mà làm cho sự phân biệt nhân tạo.
Ofek Shilon

1

Pre C ++ 11 công cụ cần ngôn ngữ để có hai loại thông tin khác nhau :

  • loại biểu thức
  • loại biến như đã được khai báo

Do bản chất của thông tin này, các tính năng phải được thêm vào bằng ngôn ngữ (không thể thực hiện được trong thư viện). Điều đó có nghĩa là (các) từ khóa mới. Các tiêu chuẩn có thể đã giới thiệu hai từ khóa mới cho điều này. Ví dụ, exprtypeđể có được loại biểu thức và decltypeđể có được kiểu khai báo của một biến. Đó sẽ là lựa chọn rõ ràng, hạnh phúc.

Tuy nhiên, ủy ban tiêu chuẩn luôn cố gắng hết sức để tránh đưa các từ khóa mới vào ngôn ngữ nhằm giảm thiểu việc phá vỡ mã cũ. Khả năng tương thích ngược là một triết lý cốt lõi của ngôn ngữ.

Vì vậy, với C ++ 11, chúng tôi chỉ có một từ khóa được sử dụng cho hai thứ khác nhau : decltype. Cách nó khác biệt giữa hai cách sử dụng là bằng cách đối xử decltype(id-expression)khác nhau. Đó là một quyết định có ý thức của ủy ban, một sự thỏa hiệp (nhỏ).


Tôi nhớ đã nghe điều này trên một cuộc nói chuyện cpp. Tuy nhiên tôi không có hy vọng tìm được nguồn. Sẽ thật tuyệt nếu ai đó tìm thấy nó.
bolov

1
C ++ trong lịch sử đã thêm một loạt các từ khóa mới, nhiều từ khóa gạch dưới và một số từ có một từ tiếng Anh. Sự hợp lý là thực sự vô lý.
tò mò

@cquilguy mỗi từ khóa được giới thiệu sẽ xung đột với các ký hiệu người dùng hiện có để ủy ban đặt nặng vào quyết định thêm từ khóa dành riêng mới vào ngôn ngữ. Nó không vô lý imo.
bolov

Không đúng: exportđã được giới thiệu. Nếu bạn có thể có export(trước đây tất cả các mẫu được mặc định là "xuất"), bạn có thể có những thứ như decltypeconstexpr. Rõ ràng việc thêm registervào một ngôn ngữ khác sẽ có vấn đề.
tò mò

@bolov Cảm ơn bạn! (1) Đây là suy đoán hay kiến ​​thức? Bạn có biết các cuộc thảo luận trong đó việc tránh một từ khóa phụ là động lực? (2) Bạn có thể đưa ra một ví dụ trong đó một biểu thức id thực sự cần được xử lý khác nhau nếu nó được sử dụng "như một biểu thức" không? (Điều này thậm chí có nghĩa là gì?)
Ofek Shilon
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.