2013-02-22 31 views
5

Ví dụ của tôi dưới đây cho thấy rằng chuyển đổi tiềm ẩn từ các loại không phải mẫu đến các loại mẫu sẽ không hoạt động trơn tru như các loại chỉ liên quan đến các loại không phải mẫu. Có cách nào để làm cho họ làm việc dù sao?Chuyển đổi ẩn thành mẫu

Ví dụ:

struct point; 

template<unsigned d> struct vec { 
    vec() { } 
    // ... 
}; 

template<> struct vec<2> { 
    vec() { } 
    vec(const point& p) { /* ... */ } // Conversion constructor 
    // ... 
}; 

struct point { 
    operator vec<2>() { return vec<2>(/* ... */); } // Conversion operator 
}; 

template<unsigned d> vec<d> foo(vec<d> a, vec<d> b) { 
    return vec<d>(/* ... */); 
} 

template<unsigned d1, unsigned d2> 
vec<d1 + d2> bar(vec<d1> a, vec<d2> b) { 
    return vec<d1 + d2>(/* ... */); 
} 

int main(int argc, char** argv) { 
    point p1, p2; 
    vec<2> v2; 
    vec<3> v3; 
    foo(v2, p1); 
    foo(p2, v2); 
    foo(p1, p2); 
    bar(v3, p1); 
} 

Có cách nào để cho mã này tự động chuyển đổi point-vec<2>?

Tôi biết tôi có thể quá tải foobar để cho phép point lập luận, ủy thác cho việc thực hiện vec sử dụng một chuyển đổi rõ ràng. Nhưng làm điều này cho tất cả các kết hợp tham số sẽ trở nên tẻ nhạt, đặc biệt đối với các hàm có nhiều tham số như vậy. Vì vậy, tôi không quan tâm đến các giải pháp mà tôi phải lặp lại mã cho mỗi kết hợp tham số của mọi chức năng.

Dường như cả nhà xây dựng chuyển đổi lẫn toán tử truyền đều không đủ để đạt được điều này. Ít nhất gcc 4.7.1 của tôi báo cáo no matching function call, mặc dù nó đặt tên cho chức năng mong muốn trong thông báo, nêu rõ rằng ‘point’ is not derived from ‘vec<d>’.

+0

Dòng nào cho lỗi đó? Ngoài ra, 'unsigned' không phải là kiểu C++. –

+0

@James: Các lỗi được báo cáo cho các dòng có lời gọi hàm, mặc dù các thông báo cũng đề cập đến các dòng khác nhau. Vui lòng sao chép và biên dịch mã ở trên, vì nó là khép kín. Phần 3.9.1 của [tiêu chuẩn C++ 11] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf) tên 'unsigned', vậy tại sao đó không phải là loại C++, được chia sẻ với C? – MvG

+0

Không, nó khai báo 'unsigned char',' unsigned short int', 'unsigned int',' unsigned long int' và 'unsigned long long int' như các loại số nguyên không dấu. –

Trả lời

8

Không có cách nào trực tiếp để có được chuyển đổi point-vec<2>, vì vào thời điểm khi các chức năng cuộc gọi foo(v1,p1) được xử lý, một chức năng foo rằng hy vọng một vec<2> như là đối số thứ hai không tồn tại được nêu ra. Nó chỉ là một mẫu chức năng, và để cho nó được khởi tạo đến một foo(const vec<2> &,const vec<2> &), một cuộc gọi hàm với các kiểu đối số chính xác này sẽ phải được đưa ra.

Để mã để làm việc, trình biên dịch sẽ phải đoán cả làm thế nào để nhanh chóng các thông số mẫu, loại lập luận point chuyển đổi sang. Điều này là quá nhiều trong trường hợp chung (mặc dù trong mã cụ thể của bạn nó xuất hiện đơn giản, bởi vì không có cách nào khác có thể để giải thích ý định của lập trình viên).

Về giải quyết này, điều duy nhất tôi có thể nghĩ đến là tạo ra các chức năng chuyển đổi cao templated:

template <typename T> 
struct make_vec 
{ }; 

template <unsigned d> 
struct make_vec<vec<d>> 
{ 
    static constexpr unsigned dim = d; 
    using type = vec<dim>; 

    static const type &from(const type &v) 
    { return v; } 
}; 

template <> 
struct make_vec<point> 
{ 
    static constexpr unsigned dim = 2; 
    using type = vec<dim>; 

    static type from(const point &p) 
    { return type(p); } 
}; 

template <typename T> 
typename make_vec<typename std::decay<T>::type>::type make_vec_from(T&& arg) 
{ return make_vec<typename std::decay<T>::type>::from(std::forward<T>(arg)); } 

Và sau đó thực hiện các chức năng foobar như mẫu chung (chấp nhận tất cả các loại định dạng,

namespace detail { 
    /* Your original implementation of foo. */ 
    template<unsigned d> vec<d> foo(vec<d>, vec<d>) { 
    return vec<d>(/* ... */); 
    } 
} 

/* Templated version of foo that calls the conversion functions (which do 
    nothing if the argument is already a vec<d>), and then calls the 
    foo() function defined above. */ 
template <typename T, typename... Ts> 
typename make_vec<typename std::decay<T>::type>::type foo(T&& arg, Ts&&... args) 
{ return detail::foo(make_vec_from(arg),make_vec_from(args)...); } 

trong trường hợp: chỉ vec<d>, sử dụng make_vec định nghĩa ở trên để chuyển đổi các loại cho đúng loại vec<d>) khôngbạn cũng cần một cách để tính toán kiểu trả về, là vec<d1+d2+d3...>. Đối với điều này, một máy tính tiền là cần thiết, cũng templated:

template <typename... Ts> 
struct dsum { 
    static constexpr unsigned value = 0; 
}; 

template <typename T, typename... Ts> 
struct dsum<T,Ts...> { 
    static constexpr unsigned value = make_vec<typename std::decay<T>::type>::dim + dsum<Ts...>::value; 
}; 

Sau đó, kiểu trả về của bar()vec<dsum<T,Ts...>::value>.

Một ví dụ làm việc hoàn toàn là ở đây: http://liveworkspace.org/code/nZJYu$11

Không hẳn đơn giản, nhưng có thể là giá trị của nó nếu bạn thực sự có rất nhiều kết hợp khác nhau của các đối số.

+0

@MvG Tôi đã sửa đổi "ví dụ hoạt động đầy đủ". Triển khai 'bar' của tôi đã sai trong phiên bản đầu tiên. – jogojapan

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