2011-09-11 40 views
5

Tôi muốn chỉ định chuyển đổi từ A<T>::B thành A<U>::B.hàm tạo bản sao tổng quát trong lớp bên trong

template<typename T> 
struct A 
{ 
    struct B 
    { 
     B() {} 

     template<typename U> 
     B(const typename A<U>::B& rhs) {} 
    }; 
}; 

int main() 
{ 
    A<int>::B x; 
    A<double>::B y = x; 
} 

Tôi nghĩ điều này sẽ làm điều đó, nhưng tôi nhận được lỗi biên dịch:

conversion from ‘A<int>::B’ to non-scalar type ‘A<double>::B’ requested"

Tại sao không phải là mã của tôi chính xác, và đúng cách để đạt được các chuyển đổi mong muốn là gì?

+0

Để làm rõ, chuyển đổi mong muốn là gì? Bởi vì bạn không chuyển đổi int thành double, nhưng là A :: B thành A :: B, là một lớp đến lớp khác. – SSight3

+0

@ SSight3: hãy để tôi đoán Robert đang cố gắng thực hiện lại các con trỏ thông minh với các số liệu bên ngoài và các phép gán đa hình ... – sehe

+0

Chuyển đổi từ A :: B thành A :: B cho mọi T, U. int và double chỉ là những ví dụ tôi đã sử dụng. Và không, sehe, đó không phải là những gì tôi đang làm. –

Trả lời

3

Hơi sửa đổi mã nguồn, để chứng minh rằng đây là khoảng giới hạn với hệ thống loại trừ:

template<typename T> 
struct A 
{ 
    struct B 
    { 
     B() {} 

     template<typename U> 
      B(const typename A<U>::B& rhs) {} 

     template<typename U> 
      B& operator=(const typename A<U>::B& rhs) {} 

     template<typename U> 
      B& something(const typename A<U>::B& rhs) {} 
    }; 
}; 

int main() 
{ 
    A<int>::B x; 

    A<double>::B y(x);  // fails to deduce 
    A<double>::B y = x; // fails to deduce 
    A<double>::B y; y = x; // fails to deduce 

    x.something(y);  // fails to deduce 
    x.something<double>(y);// NO PROBLEM 
} 

Bạn thấy rằng khi chúng ta giúp trình biên dịch một chút, không có vấn đề hơn. Cũng lưu ý các lỗi biên dịch thực tế (gcc) cho thấy đó là sự nhầm lẫn:

test.cpp|24 col 15| note: candidate is: 
test.cpp|15 col 44| note: template<class U> A<T>::B& A<T>::B::something(const typename A<U>::B&) 
[with U = U, T = int, A<T>::B = A<int>::B, typename A<U>::B = A<T>::B] 

Chú ý phần nơi U = U (ví dụ chưa được giải quyết)

4

Một mẫu không thể là một constructor sao chép. §12.8/2, chú thích:

Because a template constructor is never a copy constructor, the presence of such a template does not suppress the implicit declaration of a copy constructor. Template constructors participate in overload resolution with other constructors, including copy constructors, and a template constructor may be used to copy an object if it provides a better match than other constructors.

Kể từ khi lập luận của mẫu của bạn là const &, chữ ký của nó sẽ được chính xác giống như chức năng ngầm-tuyên bố trong trường hợp sao chép, vì vậy nó sẽ không bao giờ được sử dụng như một constructor sao chép.

Điều đó có thể được chấp nhận, bởi vì trong ví dụ bạn đang sử dụng nó làm công cụ xây dựng chuyển đổi. Tuy nhiên, đối số mẫu trước :: là ngữ cảnh không được suy luận, do đó trình biên dịch không thể cắm vào số A<int>::B và giải quyết int. Bởi vì các cách khác nhau của các mẫu chuyên biệt, không có cách nào cho trình biên dịch để tìm ra A, nếu có, đủ điều kiện. Bạn có thể thêm typedef A<int>::B B; bên trong A<float> và sau đó cả intfloat sẽ đủ điều kiện là U.

Bạn có thể sửa lỗi này bằng cách sử dụng SFINAE và thêm các loại thành viên vào các lớp để giúp điều hướng phân cấp. Đây là một demo.

#include <typeinfo> 
#include <iostream> 

template<typename T> 
struct A 
{ 
    typedef T type; 

    struct B 
    { 
     B() {} 

     template<typename U> 
     B(const U& rhs, typename U::nest_A_parent * = NULL) { 
      std::cout << "copied from type " 
      << typeid(typename U::nest_A_parent::type).name() << '\n'; 
     } 

    private: 
     typedef A nest_A_parent; 
     template< typename U > 
     friend struct B; 
    }; 
}; 

int main() 
{ 
    A<int>::B x; 
    A<double>::B y(x); 
} 
2

Làm thế nào để làm một constructor chuyển đổi (như Potatoswatter chỉ ra, nó không thể là một constructor sao chép theo định nghĩa) mà chỉ phù hợp với lồng nhau kiểu B, đối với bất kỳ A<T>::B:

namespace detail { 

// private type to identify all A<T>::B 
struct B {}; 

} // detail 

// trait to identify all A<T>::B 
// a template alias could also be used here 
template<typename T> 
struct is_b: std::is_base_of<detail::B, T> {}; 

template<typename T> 
struct A 
{ 
    struct B: detail::B { 
     B() {} 

     template<typename U> 
     B(U&& u) 
     { 
      static_assert(is_b<typename std::decay<U>::type>::value 
          , "Conversion only allowed from A<T>::B"); 
     } 
    }; 
}; 

Kỹ thuật này có lợi thế là nó không sử dụng SFINAE để nỗ lực chuyển đổi không hợp lệ sẽ được báo cáo qua static_assert thay vì không âm thầm. Mặt khác, bạn sẽ cần SFINAE nếu có ít nhất một hàm tạo chuyển đổi mẫu khác.

Giả định rằng A<T>::B sẽ khác với A<U>::B (cho các số khác nhau TU). Nếu các loại lồng nhau giống hệt nhau (như trường hợp trong mã ví dụ đơn giản), bạn sẽ được phục vụ tốt hơn bằng cách xác định B ở một nơi khác và sử dụng typedef some_private_namespace::B B; trong A.

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