2015-02-18 13 views
6

Hãy xem xét các đoạn mã sau:chuyển đổi ngầm từ int để shared_ptr

#include <iostream> 
#include <memory> 

void f(std::shared_ptr<int> sp) {} 

template <typename FuncType, typename PtrType> 
auto call_f(FuncType f, PtrType p) -> decltype(f(p)) 
{ 
    return f(p); 
} 

int main() 
{ 
    f(0); // doesn't work for any other int != 0, thanks @Rupesh 
    // call_f(f, 0); // error, cannot convert int to shared_ptr 
} 

Trong dòng đầu tiên trong main(), số nguyên 0 được chuyển thành một std::shared_ptr<int> và cuộc gọi f(0) thành công mà không cần bất kỳ vấn đề. Tuy nhiên, bằng cách sử dụng một mẫu để gọi chức năng làm cho mọi thứ khác nhau. Dòng thứ hai sẽ không biên dịch nữa, lỗi là

error: could not convert 'p' from 'int' to 'std::shared_ptr<int>' 

Câu hỏi của tôi là:

  1. Tại sao cuộc gọi đầu tiên thành công và lần thứ hai không? Có điều gì tôi thiếu ở đây không?
  2. Tôi cũng không hiểu cách chuyển đổi từ int sang std::shared_ptr đang được thực hiện trong cuộc gọi f(0), vì có vẻ như std::shared_ptr chỉ có các nhà thầu rõ ràng.

PS: Một biến thể của ví dụ này xuất hiện trong Scott Meyers' hiệu quả Modern C++ khoản 8, như một cách để bảo vệ cuộc gọi như vậy với nullptr.

+0

@@ vsoftco bạn nên đề cập đến rằng nó đang làm việc chỉ cho 'f (0)', ngoài việc không có gì được biên soạn, cùng lỗi cho khác giá trị ngay cả trong cuộc gọi đầu tiên. –

+0

[This] (http://en.cppreference.com/w/cpp/language/nullptr) gợi ý giải thích - "Có tồn tại chuyển đổi ngầm định từ nullptr thành giá trị con trỏ null của bất kỳ loại con trỏ nào và bất kỳ con trỏ nào thành loại thành viên. Chuyển đổi tương tự tồn tại cho bất kỳ giá trị nào của loại std :: nullptr_t cũng như cho macro NULL, hằng số con trỏ null. " Nhưng nó sẽ là thú vị để xem nó giải thích với các tài liệu tham khảo tiêu chuẩn và rõ ràng hơn. – Pradhan

+1

@RupeshYadav. làm xong. Thật vậy, nó là rất lạ, có vẻ như một số loại chuyển đổi con trỏ tiềm ẩn đang được thực hiện. – vsoftco

Trả lời

5

std::shared_ptr có một constructor mà mất std :: nullptr_t, đen 0 là một con trỏ hằng rỗng đó là convertiable để std :: nullptr_t từ dự thảo tiêu chuẩn C++ phần 4.10[conv.ptr] (tôi nhấn mạnh đi về phía trước):

một con trỏ hằng null là một biểu thức hằng số tích phân (5.19) prvalue kiểu integer mà đánh giá bằng không hoặc một prvalue loại std :: nullptr_t.Một hằng số con trỏ null có thể được chuyển đổi thành kiểu con trỏ ; kết quả là giá trị con trỏ null của kiểu đó và là có thể phân biệt với mọi giá trị khác của con trỏ đối tượng hoặc hàm kiểu con trỏ. Chuyển đổi như vậy được gọi là chuyển đổi con trỏ null. Hai giá trị con trỏ null cùng loại sẽ so sánh bằng nhau. Việc chuyển đổi hằng số con trỏ null thành con trỏ tới loại có độ phân giải cv là một chuyển đổi duy nhất và không phải là chuỗi của con trỏ chuyển đổi, sau đó là chuyển đổi đủ điều kiện (4.4). A null hằng số con trỏ của loại tích phân có thể được chuyển đổi thành giá trị của loại std :: nullptr_t. [Lưu ý: Giá trị kết quả không phải là giá trị con trỏ null. lưu ý end]

trong trường hợp thứ hai của bạn p đang được suy luận as type int mà mặc dù có giá trị bằng không không còn là một con trỏ hằng rỗng và do đó không phù hợp cùng kiểu chữ.

Như T.C. chỉ ra những từ ngữ được thay đổi với DR 903 đòi hỏi một số nguyên theo nghĩa đen với giá trị zero như trái ngược với một không thể thiếu biểu thức hằng mà đánh giá bằng không:

Một con trỏ hằng null là một số nguyên chữ (2.14.2) với giá trị không hoặc giá trị của loại std :: nullptr_t. Một hằng số con trỏ null có thể được chuyển đổi thành kiểu con trỏ; kết quả là con trỏ null là giá trị của loại đó và có thể phân biệt với mọi giá trị khác của con trỏ đối tượng hoặc kiểu con trỏ hàm.

+0

Có thể muốn sử dụng bản nháp được cập nhật nhiều hơn (xem câu trả lời của Casey bên dưới). IIRC "hằng số biểu thức hằng số (5.19) prvalue của kiểu số nguyên ước lượng bằng không" bao gồm đá quý như '(3 * 5 + 8 - 7)/4 - 4', và tương tác kỳ quặc với độ phân giải quá tải cho các cuộc gọi không phụ thuộc liên quan đến không gõ các đối số mẫu, vì vậy chúng đã thay đổi nó thành chữ số 0 chỉ trong DR. –

+0

@ T.C. cảm ơn bạn đã chỉ ra rằng, cập nhật câu trả lời của tôi. –

+0

Wow, đó là một số đoạn mã lớn mà họ đã bỏ phiếu. –

2

mỗi [conv.ptr]/1 (trích dẫn N4296 đây):

Một rỗng con trỏ hằng là một số nguyên chữ (2.13.2) với giá trị zero hoặc một prvalue loại std::nullptr_t. ... Hằng số con trỏ null của kiểu tích phân có thể được chuyển đổi thành giá trị của loại std::nullptr_t.

shared_ptr có một constructor không rõ ràng chấp nhận std::nullptr_t mỗi [util.smartptr.shared.const]/1:

constexpr shared_ptr(nullptr_t) noexcept : shared_ptr() { } 

mà xây dựng một sản phẩm nào, không sở hữu shared_ptr.

Khi bạn gọi f(0) trực tiếp, 0 là một rỗng con trỏ hằng được ngầm chuyển đổi sang shared_ptr<int> bởi các nhà xây dựng ở trên. Khi bạn gọi số call_f(f, 0), loại 0 chữ được suy ra thành int và dĩ nhiên không thể chuyển đổi int thành số shared_ptr<int>.

1

Gọi linh sam f (0) được biên dịch thành f (nullptr), điều này là tốt cho trình biên dịch (nhưng không phải theo ý kiến ​​của tôi). Cuộc gọi thứ hai sẽ tạo ra khai báo cho một hàm để làm việc trên bất kỳ int nào, đó là bất hợp pháp.

Buồn cười là, mà ngay cả mã này hoạt động:

f(3-3); 
f(3*0); 
+0

Có, nó có ý nghĩa sau khi đọc các câu trả lời ở trên. Mã 'f (3-3)' là ok, vì '3-3' hoặc' 3 * 0' là giá trị được đánh giá là 0. "Hằng số con trỏ null là một biểu thức hằng số không thể tách rời (5.19) prvalue của kiểu số nguyên đánh giá bằng 0 hoặc giá trị của std :: nullptr_t. " Mặt khác, 'int x = 0; f (x); 'sẽ không hoạt động nữa, vì' x' không phải là 0 prvalue nữa mà là một lvalue, do đó không phải là hằng số con trỏ null nữa. – vsoftco

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