2013-06-03 35 views
12

Tôi đang cố gắng sử dụng khấu trừ đối số mẫu với thừa kế và std::shared_ptr. Như bạn có thể thấy trong mã mẫu bên dưới, tôi đang chuyển một hàm shared_ptr<Derived> đến một hàm không phải là thành viên có khuôn mẫu, nên thực hiện khấu trừ đối số mẫu. Nếu tôi đặt tên theo cách thủ công thì mọi thứ sẽ hoạt động, và nếu tôi để cho nó thực hiện việc trích đối số mẫu thì nó không. Nó sẽ dường như như thể trình biên dịch không thể tìm ra loại, tuy nhiên thông báo lỗi cho thấy rằng nó đã làm. Tôi không chắc những gì đang xảy ra ở đây, và tôi sẽ đánh giá cao bất kỳ đầu vào nào. (Visual Studio 2010)Vấn đề với std :: shared_ptr, thừa kế, và khấu trừ đối số mẫu

#include <memory> 

template <typename T> 
class Base {}; 

class Derived : public Base<int> {}; 

template <typename T> 
void func(std::shared_ptr<Base<T> > ptr) {}; 

int main(int argc, char* argv[]) 
{ 
    std::shared_ptr<Base<int>> myfoo(std::shared_ptr<Derived>(new Derived)); // Compiles 
    func(myfoo); // Compiles 
    func<int>(std::shared_ptr<Derived>(new Derived)); // Compiles 
    func(std::shared_ptr<Derived>(new Derived)); // Doesn't compile. The error message suggests it did deduce the template argument. 

    return 0; 
} 

Các thông báo lỗi:

5> error C2664: 'func' : cannot convert parameter 1 from 'std::tr1::shared_ptr<_Ty>' to 'std::tr1::shared_ptr<_Ty>' 
5>   with 
5>   [ 
5>    _Ty=Derived 
5>   ] 
5>   and 
5>   [ 
5>    _Ty=Base<int> 
5>   ] 
5>   Binding to reference 
5>   followed by 
5>   Call to constructor 'std::tr1::shared_ptr<_Ty>::shared_ptr<Derived>(std::tr1::shared_ptr<Derived> &&,void **)' 
5>   with 
5>   [ 
5>    _Ty=Base<int> 
5>   ] 
5>   c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\memory(1532) : see declaration of 'std::tr1::shared_ptr<_Ty>::shared_ptr' 
5>   with 
5>   [ 
5>    _Ty=Base<int> 
5>   ] 
5> 
+2

Dưới đây là những gì GCC 4.7.3 phải nói về nó, FYI: 't.cpp: 9: 6: lưu ý: đối số/thay thế đối số mẫu không thành công: t.cpp: 16: 47: lưu ý: các loại không khớp Cơ sở 'và' Có nguồn gốc ' t.cpp: 16: 47: lưu ý:' std :: shared_ptr 'không bắt nguồn từ' std :: shared_ptr > '' –

Trả lời

10

Trong khi trình biên dịch có thể thực hiện bắt nguồn-to-base chuyển đổi khi thực hiện loại trừ, std::shared_ptr<Derived> không không bản thân xuất phát từ std::shared_ptr<Base<int>>.

Có chuyển đổi do người dùng xác định giữa hai loại cho phép shared_ptr hoạt động như con trỏ thông thường đối với đa hình, nhưng trình biên dịch sẽ không tính đến chuyển đổi do người dùng xác định khi thực hiện khấu trừ loại.

Nếu không xem xét người dùng định nghĩa conversiosn, trình biên dịch không thể suy ra một T mà có thể làm shared_ptr<Base<T>> hoặc giống với shared_ptr<Derived> hoặc một lớp cơ sở của shared_ptr<Derived> (một lần nữa, shared_ptr<Base<int>>không một lớp cơ sở của shared_ptr<Derived>).

Do đó, loại khấu trừ không thành công.

Để làm việc xung quanh vấn đề này, bạn có thể cho các tham số của hàm của bạn là một đơn giản shared_ptr<T> thêm một SFINAE-ràng buộc sẽ đảm bảo tình trạng quá tải của bạn được chọn chỉ khi loại đối số có nguồn gốc từ (hoặc là) một mẫu của mẫu lớp Base:

#include <type_traits> 

namespace detail 
{ 
    template<typename T> 
    void deducer(Base<T>); 

    bool deducer(...); 
} 

template <typename T, typename std::enable_if< 
    std::is_same< 
     decltype(detail::deducer(std::declval<T>())), 
     void 
     >::value>::type* = nullptr> 
void func(std::shared_ptr<T> ptr) 
{ 
    // ... 
} 

Đây là live example.

+0

Thật không may,' decltype' et al. không có sẵn trong MSVC10. Tại sao bạn không sử dụng 'is_base_of'? – dyp

+1

@DyP: 'decltype' et al. * có sẵn trong MSVC10 –

+0

@DyP: Huh, có vẻ như chỉ có 'decltype' có sẵn, không phải" al .: ": D Vâng, trong trường hợp đó tôi sẽ cần phải thay đổi mọi thứ một chút, bạn đang phải –

0

Nó hoạt động nếu bạn viết nó theo cách này:

template <typename T> 
void func(std::shared_ptr<T> ptr) {}; 

Nếu bạn thực sự muốn chặn một cách rõ ràng hàm từ được gọi với một cái gì đó không bắt nguồn từ cơ sở, bạn có thể sử dụng type_traits/enable_if/etc.

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