2014-07-20 27 views
26

Với mã nguồn sau đây:trình biên dịch nào có lỗi, theo tiêu chuẩn?

#include <memory> 
#include <iostream> 

using namespace std; 

struct concept 
{ 
    virtual void perform() = 0; 
}; 


struct model : concept, enable_shared_from_this<model> 
{ 
    void perform() override { 
     cout << "my pointer is " << shared_from_this().get() << endl; 
    } 
}; 

int main(int argc, const char * argv[]) 
{ 
    // shared_ptr<concept> concept_ptr = make_shared<model>(); 
    shared_ptr<concept> concept_ptr { new model }; 
    concept_ptr->perform(); 
    return 0; 
} 

Biên soạn dưới gcc, mã này biên dịch và liên kết nội bộ weak_ptr với địa chỉ của model.

Dưới clang mã sẽ không biên dịch (thông báo lỗi bao gồm ở cuối)

Nếu bạn thay thế khởi động của concept_ptr với shared_ptr<concept> concept_ptr = make_shared<model>(); nó sẽ biên dịch trên cả hai.

Điều gì là chính xác?

chỉnh sửa:

phiên bản của tôi về kêu vang là một trong những mà tàu với Xcode:

$ clang --version 
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn) 
Target: x86_64-apple-darwin13.3.0 
Thread model: posix 

edit2:

Chỉ muốn nói lời cảm ơn đến tất cả mọi người đóng góp. Nếu bạn quan tâm, lý do tôi muốn làm điều này là tôi muốn có một giao diện mờ để triển khai với ngữ nghĩa xử lý được chia sẻ. Một số triển khai (các đối tượng không đồng bộ) yêu cầu đối tượng gọi lại đảm bảo rằng đối tượng triển khai vẫn tồn tại (lập luận cho shared_from_thisweak_ptr::lock). Các triển khai khác không yêu cầu điều này. Tôi muốn tránh làm lu mờ khái niệm (giao diện công khai) với lớp cơ sở enable_shared_from_this<>, vì các cặp vợ chồng thực hiện với giao diện - một cái ác đã biết.

Trong hầu hết các trường hợp, bạn nên sử dụng make_shared để tạo đối tượng triển khai. Trong những trường hợp hiếm đòi hỏi một destructor tùy chỉnh, sau đây dường như di động:

auto concept_ptr = static_pointer_cast<concept>(shared_ptr<model> { 
                 new model , 
                 [](model* self) { 
                  // some_deletion_operation on self; 
                 } }); 

phụ lục: thông báo lỗi trên kêu vang:

In file included from /Users/richardh/Documents/dev/Scratchpad/tryit/tryit/try2.cpp:1: 
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/memory:4013:35: error: no viable overloaded '=' 
      __e->__weak_this_ = *this; 
      ~~~~~~~~~~~~~~~~~^~~~~~ 
...etc...  
+1

+1 câu hỏi hay –

+0

Cung cấp các lệnh biên dịch và phiên bản trình biên dịch. –

+0

@LightnessRacesinOrbit nó chỉ ra đó là một sự khác biệt trong việc thực hiện libstdC++ và libC++. –

Trả lời

11

Tôi hiểu rằng libstdC++ sau tiêu chuẩn chặt chẽ hơn ở đây.

Liên quan đến yêu cầu cho

shared_ptr<T> shared_from_this(); 
shared_ptr<const T> shared_from_this() const; 

cả N3337 §20.7.2.4 (7) và N3936 §20.8.2.5 (7) chỉ đòi hỏi

enable_shared_from_this<T> sẽ là một lớp cơ sở tiếp cận của T. *this phải là một đối tượng con của một đối tượng t loại T. Sẽ có ít nhất một shared_ptr phiên bản p sở hữu &t.

Không có yêu cầu đặt tên rằng một shared_ptr sở hữu &t thực sự có phải là một shared_ptr<T> hoặc shared_ptr<A_to_T_Convertible>.

Và chức năng đó chính là cốt lõi của chức năng của lớp đó.

Như vậy, do Tp như param thực tế của enabled_shared_from_thisTp1 như các tham số thực tế mà sở hữu shared_ptr, is_convertible<Tp1, Tp>::value == true, chứ chưa nói is_same<Tp1, Tp>::value == true, không được yêu cầu của tiêu chuẩn, tương tự cho con trỏ tương ứng.


Và quả thực, đầy đủ sản lượng vang ++ sử dụng ++ libc có

/usr/local/bin/../include/c++/v1/memory:3997:35: error: no viable overloaded '=' 
       __e->__weak_this_ = *this; 
       ~~~~~~~~~~~~~~~~~^~~~~~ 
/usr/local/bin/../include/c++/v1/memory:4035:5: note: in instantiation of 
     function template specialization 
     'std::__1::shared_ptr<concept>::__enable_weak_this<model>' requested here 
    __enable_weak_this(__p); 
    ^
[...]enable_shared.cxx:34:25: note: in instantiation 
     of function template specialization 
     'std::__1::shared_ptr<concept>::shared_ptr<model>' requested here 
    shared_ptr<concept> model_ptr1(new model); 
         ^
/usr/local/bin/../include/c++/v1/memory:4942:15: note: candidate function not 
     viable: no known conversion from 'std::__1::shared_ptr<concept>' to 'const 
     std::__1::weak_ptr<model>' for 1st argument 
    weak_ptr& operator=(weak_ptr const& __r) _NOEXCEPT; 
      ^
/usr/local/bin/../include/c++/v1/memory:4953:15: note: candidate function not 
     viable: no known conversion from 'std::__1::shared_ptr<concept>' to 
     'std::__1::weak_ptr<model>' for 1st argument 
    weak_ptr& operator=(weak_ptr&& __r) _NOEXCEPT; 
      ^
/usr/local/bin/../include/c++/v1/memory:4949:9: note: candidate template 
     ignored: could not match 'weak_ptr' against 'shared_ptr' 
     operator=(weak_ptr<_Yp> const& __r) _NOEXCEPT; 
     ^
/usr/local/bin/../include/c++/v1/memory:4960:9: note: candidate template 
     ignored: could not match 'weak_ptr' against 'shared_ptr' 
     operator=(weak_ptr<_Yp>&& __r) _NOEXCEPT; 
     ^
/usr/local/bin/../include/c++/v1/memory:4967:13: note: candidate template 
     ignored: disabled by 'enable_if' [with _Yp = concept] 
      is_convertible<_Yp*, element_type*>::value, 
      ^

Vì vậy libC++ đây muốn

is_convertible<Tp1* /*= Base* = concept**/, Tp* /*= Derived* = model* */> 

trong đó tất nhiên không ở đây, mà thời gian chạy *this đó rất shared_ptr<Tp1> sẽ là dynamic_cast -able đến Tp* không thuộc ansatz tại đây.


Từ quan điểm này, cũng rõ ràng lý do tại sao shared_ptr<concept> concept_ptr = make_shared<model>(); không bị ảnh hưởng; trên số rhs có một constructor shared_ptr<Tp /* = derived = model */> và cho rằng ptris_convertible giữ.


libstdC++ không bị này, bởi vì nó vượt qua luận, do đó loại của nó (= nguồn gốc = mô hình), của các nhà xây dựng shared_ptr<Tp1 /* = Base = concept*/> xuống weak_ptr<T /*= Derived = model*/> phân công nội bộ, không phải là shared_ptr trong xây dựng.

https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h#L848

template<typename _Tp, _Lock_policy _Lp> 
    class __shared_ptr 
{ 

https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h#L858

template<typename _Tp1> 
explicit __shared_ptr(_Tp1* __p) 
    : _M_ptr(__p), _M_refcount(__p) 
{ 
    __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>) 
    static_assert(!is_void<_Tp1>::value, "incomplete type"); 
    static_assert(sizeof(_Tp1) > 0, "incomplete type"); 
    __enable_shared_from_this_helper(_M_refcount, __p, __p); 
} 

https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h#L1459

template<typename _Tp, _Lock_policy _Lp> 
    class __enable_shared_from_this 
    { 

https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h#L1482

private: 
    template<typename _Tp1> 
void 
_M_weak_assign(_Tp1* __p, const __shared_count<_Lp>& __n) const noexcept 
{ _M_weak_this._M_assign(__p, __n); } 


    template<typename _Tp1> 
friend void 
__enable_shared_from_this_helper(const __shared_count<_Lp>& __pn, 
       const __enable_shared_from_this* __pe, 
       const _Tp1* __px) noexcept 
{ 
    if (__pe != 0) 
    __pe->_M_weak_assign(const_cast<_Tp1*>(__px), __pn); 
} 

Quan điểm của tôi chỉ; bình luận chào mừng.

@Richard Hodges: +1, chủ đề rất thú vị

+0

Đây là một cuộc điều tra thực sự kỹ lưỡng, cảm ơn bạn. Tôi nghĩ rằng những gì bạn đang nói là trường hợp sử dụng thực tế của tôi không được bao gồm bởi tiêu chuẩn, và nó sẽ xảy ra để làm việc trên libstdC++ thông qua một sự lựa chọn may mắn của chi tiết thực hiện. –

+2

Tôi sẽ không nói rằng điều này làm việc cho libstdC++ bởi sự lựa chọn thực hiện may mắn chỉ; Tôi muốn nói nó phải làm việc. Tiêu chuẩn không chính xác nổi tiếng là trực giao, nhưng nếu nó không cắt nó thành một tập hợp nhất định các tham số kiểu hợp lệ cho việc "sở hữu" shared_ptr có liên quan, tôi sẽ đề xuất * con trỏ là con trỏ là con trỏ * tương tự của Base * p = new Derived phải là hợp pháp, bất kể có bất kỳ nội dung nào. – Solkar

+1

@TC nhận xét về câu trả lời của tôi hơn là vấn đề libC++ có liên quan đến [bug 18843] (http: // llvm.org/bugs/show_bug.cgi? id = 18843). –

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