2015-03-21 21 views
6

Tôi có mã sau đây.suy ra loại phần tử tuple trong C++ 11

template <typename... Types> 
void print_tuple(const std::tuple<Types&&...>& value) 
{ 
    std::cout << std::get<0>(value) << "," << std::get<1>(value) << std::endl; 
} 
print_tuple(std::forward_as_tuple("test",1)); 

mà trình biên dịch phàn nàn về

error: invalid initialization of reference of type ‘const std::tuple<const char (&&)[5], int&&>&’ from expression of type ‘std::tuple<const char (&)[5], int&&>’ 
    print_tuple(std::forward_as_tuple("test",1)); 

tại sao biên dịch suy ra kiểu của phần tử đầu tiên trong tuple là const char (& &) [5]?

+0

Bạn thậm chí không cần chụp một bộ dữ liệu cụ thể. Tại sao không đơn giản là 'template void print_tuple (Tuple && value)'? – 0x499602D2

+0

Vì nó không rõ ràng với ít nhất hai người: định nghĩa 'print_tuple' * của bạn * cho phép cuộc gọi nếu các đối số mẫu được chỉ định rõ ràng là' print_tuple '. Đó là một câu hỏi hay giải thích tại sao trình biên dịch lại suy ra 'Loại' đầu tiên khác nhau. – hvd

+0

@hvd Có thể là do một chuỗi ký tự là một giá trị và được chuyển tiếp như vậy. – 0x499602D2

Trả lời

2

Nói chung, để khấu trừ thành công, đối số cần phải có cùng một dạng chung như tham số. Có một số ngoại lệ trong đó T && có thể được suy ra từ U & (bằng cách chọn T = U &), nhưng không có trường hợp ngoại lệ nào được chỉ định cho trường hợp này.

14.8.2.5 Suy luận đối số mẫu từ một loại [temp.deduct.type]

8 Mẫu kiểu lập luận T, một mẫu template luận TT hoặc một đối số mẫu không loại i có thể suy luận nếu PA có một trong các hình thức sau:

[...]

T&
T&&

[...]

Nó không phải chính xác rõ ràng, nhưng điều này đòi hỏi P (tham số) và A (đối số) để cả hai đều có hình thức tương tự. Cả hai cần phải có dạng T& hoặc cả hai dạng T&&.Các trường hợp ngoại lệ, các trường hợp T && có thể được rút ra từ U &, được thực hiện bằng cách thay đổi T && để đồng bằng T trước khớp diễn ra, trong trường hợp hạn chế:

10 Tương tự, nếu P có một hình thức có chứa (T), sau đó mỗi loại thông số Pi của danh sách loại tương ứng của P được so sánh với loại thông số tương ứng Ai của thông số tương ứng tương ứng của A. Nếu PA là các loại chức năng có nguồn gốc từ khấu trừ khi lấy địa chỉ của mẫu chức năng (14.8.2.2) hoặc khi khấu trừ đối số mẫu từ khai báo hàm (14.8.2.6) và PiAi là thông số của cấp cao nhất tham số kiểu-list của PA, tương ứng, Pi được điều chỉnh nếu nó là một tài liệu tham khảo rvalue đến một tham số mẫu cv-không đủ tiêu chuẩn và Ai là một tài liệu tham khảo giá trị trái, trong trường hợp này loại Pi được thay đổi để trở thành mẫu loại thông số (ví dụ: T&& được thay đổi thành chỉ đơn giản là T). [...]

14.8.2.1 Suy luận đối số mẫu từ một cuộc gọi chức năng [temp.deduct.call]

3 [...] Nếu P là một tham chiếu rvalue cho một tham số mẫu cv-unqualified và đối số là một giá trị, loại "tham chiếu lvalue đến A" được sử dụng thay cho A để khấu trừ loại. [...]

nhưng không có ngoại lệ tương tự nào áp dụng cho trường hợp của bạn.

Đó là nguyên tắc này tương tự mà làm cho

template <typename T> struct S { }; 
template <typename T> void f(S<const T>) { } 
int main() { f(S<void()>()); } 

không hợp lệ: const T không thể được rút ra từ void(), mặc dù T = void() sẽ cung cấp chính xác những kết quả đó, và gọi f<void()> sẽ thành công.

câu trả lời đã xóa Wintermute cho thấy rằng bạn có thể sử dụng

template <typename... Types>  // vv-- change here 
void print_tuple(const std::tuple<Types...>& value) 

thay vì: điều này cho phép Types được suy luận như tài liệu tham khảo giá trị trái, như tài liệu tham khảo rvalue, hoặc như phi tài liệu tham khảo, tùy thuộc vào loại value.

0

Bạn có định sử dụng && trong std::tuple<Types&&...> làm tham chiếu chung không? Đây không phải là tài liệu tham khảo phổ quát; nó là tham chiếu rvalue và chỉ có thể liên kết với rvalues. Bạn có thể làm như thế này để kiểm tra những loại tài liệu tham khảo đó là:

template<typename T> 
class TD; 

Sau đó, xác định mẫu hàm của bạn:

template <typename... Types> 
void print_tuple(const std::tuple<Types&&...>& value) 
{ 
    TD<decltype(value)> a; 
    std::cout << std::get<0>(value) << "," << std::get<1>(value) << std::endl; 
} 

Sau đó, bạn sẽ thấy những lỗi biên dịch như:

implicit instantiation of undefined template 'TD<const std::__1::tuple<std::__1::basic_string<char> &&, int &&> &>' 

Bạn có thể thấy rằng ngay cả đối với loại int, nó suy ra là tham chiếu rvalue. Tham chiếu Rvalue không thể liên kết với các giá trị. Bạn có thể thử đó bằng cách gọi:

int i = 1; 
print_tuple(std::forward_as_tuple(i,1)); // error. 

Vì vậy, nó là đúng rằng const char(&&)[5] là suy luận, và một chuỗi chữ không thể được chuyển đổi sang const char(&&)[5]. Nếu bạn gọi print_tuple như:

print_tuple(std::forward_as_tuple(string("test"),1)); 

Nó sẽ hoạt động. Bây giờ loại là tuple<string&&, int&&>.

+0

Giống như tôi đã nhận xét về câu hỏi, nếu một cách rõ ràng đánh vần các đối số mẫu, như trong 'print_tuple ', nó hoạt động. Câu trả lời này là sai. 'T &&' không nhất thiết là một tham chiếu rvalue. Nếu 'T' là một tham chiếu lvalue, thì' T && 'chỉ là' T'. Vấn đề duy nhất là khấu trừ không hoạt động: khi 'T &&' được cho là 'U &', 'T' không được suy luận là' U & ', mặc dù nó có thể xảy ra. – hvd

+0

@hvd Không có gì trong ngôn ngữ mà nói rằng 'T' có thể được suy ra là' U & 'trong ngữ cảnh này. Các điều kiện cho [temp.deduct.call]/3 không được đáp ứng. Một ví dụ đơn giản là 'mẫu void f (const T &&)' sẽ không liên kết với các giá trị. Trừ khi tất nhiên bạn rõ ràng chuyên mẫu, mà không thực sự có liên quan trong một câu hỏi về khấu trừ đối số mẫu. – Oktalist

+0

@Oktalist Nó không yêu cầu chuyên mẫu. Nó chỉ yêu cầu xác định các đối số mẫu. Bạn có thể gọi 'f ' với biểu thức 'int' lvalue mà không gặp bất kỳ vấn đề nào, mặc dù khấu trừ sẽ không bao giờ cung cấp cho bạn' T = int & '. Tôi vừa đăng câu trả lời với một số chi tiết khác. – hvd

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