2015-04-24 21 views
16

Hãy xem xét các đoạn sau đây:Mẫu bí danh nhận dạng có thể là tham chiếu chuyển tiếp không?

template <class T> 
using identity = T; 

template <class T> 
void foo(identity<T>&&) {} 

int main() 
{ 
    int i{}; 
    foo(i); 
} 

i là một giá trị trái, vì thế nếu foo khai báo một tham số tham chiếu chuyển tiếp, cần biên dịch. Tuy nhiên, nếu identity<T>&& được chuyển thành int&&, thay vào đó, nó sẽ gây ra lỗi.

Mã biên dịch trong GCC 6.0.0 (demo).

Mã này thất bại trong việc biên dịch trong Clang 3.7.0 (demo) với thông báo lỗi:

error: no known conversion from 'int' 
to 'identity<int> &&' (aka 'int &&') for 1st argument 

Cái nào là đúng?

+1

Điều thú vị, 'danh tính ' có vẻ hoạt động trong cả GCC và Clang ([demo] (http://melpon.org/wandbox/permlink/r1YH40UcY8lwouQ4)) được coi là tham chiếu chuyển tiếp –

+0

Đó có thể là do 'T && '* là * của định dạng được chỉ định cho tham chiếu chuyển tiếp. – Angew

+1

@Angew Có, nhưng dưới dạng tham số hàm, không phải là đối số mẫu trong mẫu-id. – bogdan

Trả lời

8

xem xét mã này:

template<class T> using identity = T; 

template<class T> void foo(identity<T>&&) { } //#1 

template<class T> void foo(T&&) { } //#2 

int main() 
{ 
    int i{}; 
    foo(i); 
} 

Cả GCC và Clang từ chối nó vì #2 là một định nghĩa mới của #1. Nếu chúng thực sự là cùng một mẫu, chúng tôi có thể mong đợi #1 hoạt động theo cách chính xác giống như #2, có nghĩa là identity<T>&& sẽ hoạt động như một tham chiếu chuyển tiếp. Theo logic này, chúng ta không biết cái nào là đúng, nhưng GCC ít nhất là nhất quán.

Điều này cũng phù hợp với ví dụ rất giống với tiêu chuẩn tại [14.5.7p2].

Chúng tôi cũng nên xem xét cách khấu trừ đối số mẫu có thể hoạt động trong trường hợp này. Nếu identity là một mẫu lớp, biểu mẫu của nó có thể được so khớp với loại đối số hàm mà không nhìn vào định nghĩa của nó, cho phép trình biên dịch suy ra đối số mẫu cho T. Tuy nhiên, ở đây chúng tôi có một mẫu bí danh; Không thể khấu trừ T vào int hoặc int& hoặc bất kỳ điều gì khác trừ khi identity<T> được thay thế bằng T. Nếu không, chúng ta đang đối đầu với cái gì? Sau khi thay thế xong, tham số hàm trở thành tham chiếu chuyển tiếp.

Tất cả những điều trên đều hỗ trợ ý tưởng identity<T>&& (và identity<T&&>) được coi là tương đương với tham chiếu chuyển tiếp.

Tuy nhiên, có vẻ như có nhiều điều hơn đến việc thay thế ngay mẫu bí danh-id bằng loại id tương ứng. Đoạn [14.5.7p3] cho biết:

Tuy nhiên, nếu id mẫu phụ thuộc, đối số mẫu tiếp theo thay thế vẫn áp dụng cho id mẫu. [Ví dụ:

template<typename...> using void_t = void; 
template<typename T> void_t<typename T::foo> f(); 
f<int>(); // error, int does not have a nested type foo 

-end dụ]

này có thể dường như không có nhiều việc phải làm với ví dụ của bạn, nhưng nó thực sự chỉ ra rằng hình thức ban đầu của mẫu-id vẫn đưa vào trong một số trường hợp, độc lập với loại id thay thế. Tôi đoán điều này mở ra khả năng rằng sau này tất cả mọi thứ có thể thực sự không được coi là một tham chiếu chuyển tiếp identity<T>&&.

Khu vực này có vẻ không được xác định trong tiêu chuẩn. Điều này cho thấy trong số các vấn đề mở đối phó với các vấn đề tương tự, tất cả trong cùng một danh mục theo ý kiến ​​của tôi: trong trường hợp nào thì dạng mẫu ban đầu của mẫu-id được tính đến khi khởi tạo, mặc dù nó được thay thế bằng tương ứng với loại-id ngay lập tức khi gặp phải. Xem các vấn đề 1980, 20212025. Ngay cả các vấn đề 14301554 có thể được xem là xử lý các vấn đề tương tự.

Đặc biệt, issue 1980 chứa các ví dụ sau:

template<typename T, typename U> using X = T; 
template<typename T> X<void, typename T::type> f(); 
template<typename T> X<void, typename T::other> f(); 

với lưu ý:

CWG cảm thấy rằng hai tờ khai này không nên tương đương.

(CWG - Tổ công tác Core)

Một dòng tương tự như lập luận có thể áp dụng đối với ví dụ của bạn, làm cho identity<T>&& không tương đương với một tham chiếu chuyển tiếp. Điều này thậm chí có thể có giá trị thực tế, như một cách đơn giản để tránh tham lam của tham chiếu chuyển tiếp khi tất cả những gì bạn muốn là một tham chiếu rvalue cho một suy luận T.

Vì vậy, tôi nghĩ bạn đã nêu ra một vấn đề rất thú vị. Ví dụ của bạn có thể đáng để thêm làm ghi chú cho số issue 1980, để đảm bảo ví dụ này được tính đến khi soạn thảo giải pháp.

Theo ý kiến ​​của tôi, câu trả lời cho câu hỏi của bạn là, bây giờ, một sự vang dội "ai biết?".


Cập nhật: Trong những ý kiến ​​đến đầu kia, liên quan, question, Piotr S. chỉ ra issue 1700, được đóng như "không phải là một khiếm khuyết". Nó đề cập đến trường hợp rất giống nhau được mô tả trong câu hỏi đó, và chứa các lý do sau đây:

Bởi vì các loại của các thông số chức năng là như nhau, bất kể bằng văn bản trực tiếp hoặc thông qua một mẫu bí danh, khấu trừ phải được xử lý theo cùng một cách trong cả hai trường hợp.

Tôi nghĩ rằng nó cũng áp dụng cho các trường hợp được thảo luận ở đây và giải quyết vấn đề ngay bây giờ: tất cả các biểu mẫu này sẽ được coi là tương đương với tham chiếu chuyển tiếp. Nó sẽ rất thú vị để xem nếu điều này được thay đổi gián tiếp bởi các nghị quyết cho các vấn đề mở khác, nhưng họ chủ yếu là đối phó với thất bại thay thế hơn là khấu trừ của chính nó, vì vậy tôi đoán một hiệu ứng gián tiếp là khá khó xảy ra.)


Tất cả các tham chiếu chuẩn là bản nháp làm việc hiện tại, N4431, bản nháp thứ hai sau C++ 14 cuối cùng.

Lưu ý rằng trích dẫn từ [14.5.7p3] là một bổ sung gần đây, được bao gồm ngay sau phiên bản C++ 14 cuối cùng với độ phân giải DR1558. Tôi nghĩ chúng ta có thể mong đợi những bổ sung thêm trong lĩnh vực này khi các vấn đề khác được giải quyết theo cách này hay cách khác.

Cho đến lúc đó, có thể đáng để đặt câu hỏi này trong nhóm ISO C++ Standard - Discussion; nên mang nó đến sự chú ý của đúng người.

+0

Đoạn văn của bạn từ §14.5.7/3 không có trong tiêu chuẩn C++ 14 hoặc C++ 11. – Columbo

+0

@Columbo Vâng, đó là độ phân giải của [DR1558] (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1558), được bao gồm trong bản nháp làm việc sau tháng 11 năm 2014 cuộc họp, vì vậy nó có thể được tìm thấy trong hai bản nháp sau C++ 14 cuối cùng. Tôi quên đề cập đến phiên bản tài liệu tôi trích dẫn, tôi sẽ cập nhật câu trả lời, cảm ơn cho ghi chú. – bogdan

+0

Cảm ơn bạn đã phân tích sâu sắc này. Tôi đã bắt đầu một cuộc thảo luận về các nhóm trên google [ở đây] (https://groups.google.com/a/isocpp.org/d/topic/std-discussion/s0U6TNU1Qy0/discussion) –

7

Đây không phải là tham chiếu chuyển tiếp. C++ 14 (n4140) 14.8.2.1/3 (tôi nhấn mạnh):

... Nếu P là một tài liệu tham khảo rvalue đến một cv-không đủ tiêu chuẩn mẫu tham số và các đối số là một giá trị trái, các loại "tham chiếu lvalue để A" được sử dụng trong nơi của A cho khấu trừ loại.

Đây là phần của tiêu chuẩn chỉ định cách tham chiếu chuyển tiếp hoạt động. P, loại tham số chức năng, có loại "tham chiếu rvalue tới identity<T>". identity<T> là loại tham số mẫu, nhưng nó không phải là thông số mẫu, do đó quy tắc khấu trừ tham chiếu chuyển tiếp không áp dụng.

Chúng tôi cũng có thể nhìn vào những gì 14.5.7/2 đã nói về bí danh mẫu:

Khi một mẫu-id đề cập đến chuyên môn của một mẫu alias, nó tương đương với các liên loại thu được bằng cách thay thế mẫu-luận của nó đối với mẫu-thông số trong loại-id của bí danh mẫu.

Vì vậy, các bí danh thay là tương đương với các loại của T, nhưng 14.8.2.1/3 đọc "tài liệu tham khảo để ... mẫu tham số," không phải là "tài liệu tham khảo để ... các loại của một số mẫu . "

+1

Nếu chúng tôi giả định việc thực thi là miễn phí để thay thế mẫu bí danh bằng loại thực tế mà nó trỏ đến trước khi loại trừ đối số mẫu diễn ra (hoặc cái gì khác, không chắc chắn cách gọi nó, tôi đang nói đến GCC), đoạn này vẫn được áp dụng, phải không? - Tôi có thể sai, tôi chỉ không biết. –

+0

@MarcAndreson Bất kỳ lý do nào tại sao chúng ta có thể giả định điều đó? – Angew

+0

@MarcAndreson Mỗi 14.5.7/2, "Khi một * template-id * đề cập đến chuyên môn của mẫu bí danh, nó tương đương với ** loại ** ** được liên kết thu được bằng cách thay thế * mẫu-đối số * của nó cho * mẫu tham số * trong * type-id * của bí danh mẫu ". Một lần nữa, nó tương đương với kiểu *, nhưng tham số chuyển tiếp tham chiếu nói về các tham số mẫu, không phải kiểu của chúng. – Angew

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