2013-06-07 40 views
7

Tôi muốn có hai mẫu tương tự, một với 1 tham số và khác với 2 tham số:Tránh trùng lặp mã trong một mẫu chuyên

template<typename T1, typename T2=void> 
struct foo { 
    T1 m_t1; 
    T2 m_t2; 
    foo(T1 t1, T2 t2) : m_t1(t1), m_t2(t2) {} 
    T1 t1() { return m_t1; } 
    T2 t2() { return m_t2; } 
}; 

template<typename T1> 
struct foo<T1,void> { 
    T1 m_t1; 
    foo(T1 t1) : m_t1(t1) {} 
    T1 t1() { return m_t1; } 
}; 

Thông báo việc sao chép mã cho tất cả những thứ T1-liên quan. Làm thế nào tôi có thể tránh điều này?

+0

Mã của bạn là có sự mơ hồ nội tại ngoài vấn đề bạn đang cố gắng giải quyết: Đối với mẫu tham số đơn, trình biên dịch sẽ không thể giải quyết mẫu để sử dụng. Ví dụ: http://ideone.com/7wcB6Z – SomeWittyUsername

+0

@icepack Ý của bạn là gì? Mã của bạn không thành công, như mong đợi, bởi vì lớp không có một hàm tạo mặc định? – 7cows

Trả lời

7

Giải pháp duy nhất cho điều này là có càng nhiều mã càng tốt trong lớp cơ sở. Ví dụ:

template<typename T1> 
struct foo_base { 
    T1 m_t1; 
    explicit foo_base(T1 t1) : m_t1(t1) {} 
    T1 t1() const { return m_t1; } 
}; 

template<typename T1, typename T2=void> 
struct foo : foo_base<T1> { 
    T2 m_t2; 
    foo(T1 t1, T2 t2) : foo_base<T1>(t1), m_t2(t2) {} 
    T2 t2() const { return m_t2; } 
}; 

template<typename T1> 
struct foo<T1,void> : foo_base<T1> { 
    explicit foo(T1 t1) : foo_base<T1>(t1) {} 
}; 
+0

"Rõ ràng" có liên quan gì đến câu hỏi này? – 7cows

+2

@ 7cows: Thực hành tốt. Xem http://stackoverflow.com/questions/121162/what-does-the-explicit-keyword-in-c-mean –

+2

và trong khi bạn đang ở đó, một số công cụ chính xác cũng có thể được thêm – TemplateRex

0

Thừa kế sẽ giải quyết được sự cố của bạn. Xác định một lớp cơ sở một tham số cung cấp các công cụ T1 và làm cho phiên bản hai tham số kế thừa điều đó.

2

Đây là một loại câu hỏi rất chung chung.

Trong trường hợp này, bạn có thể đặt công cụ T1 có liên quan trong một lớp cơ sở. Nói chung, bạn nên tránh vấn đề một-hai-nhiều. Những gì bạn đang làm ở đây cũng có thể đạt được bằng std::tuple, cũng là một phần của thư viện Boost.

Đối với giá trị của nó, tuple hoạt động bằng cách soạn bất kỳ số lượng lớp cơ sở nào.

0

Có ba số: 0, 1 và vô cực.

Ồ, và việc đếm bắt đầu từ 0, không phải 1!

template<typename... Ts> 
struct first_type {} 
template<typename T0, typename... Ts> 
struct first_type { 
    typedef T0 type; 
}; 
template<typename... Ts> 
using FirstType = typename first_type<Ts...>::type; 

template<typename T0, typename Rest, typename=void> 
struct foo_impl; 

template<typename... Ts> 
struct foo_augment {}; 
template<typename T1> 
struct foo_augment<T1> { 
    T1 m_t1; 
    T1 t1() const { return m_t1; } 
    T1 t1() { return m_t1; } 
}; 

template<typename T0, typename... Ts> 
struct foo_impl< T0, std::tuple<Ts...>, typename std::enable_if< (sizeof...(Ts)<2) >::type >: 
    foo_augment<Ts...> 
{ 
    // use FirstType<Ts...> to get at the second type of your argument pack 
    foo_impl(T0 t0, Ts... ts): 
    m_t0(t0), foo_augment<Ts...>(ts...) 
    {}; 
    T0 m_t0; 
    T0 t0() { return m_t0; } 
    T0 t0() const { return m_t0; } 
}; 
template<typename T0, typename... Ts> 
using foo = foo_impl<T0, std::tuple<Ts...>>; 

Bây giờ, lưu ý rằng có rất nhiều mẫu trên, số tiền hợp lý nhiều hơn số lượng mã trùng lặp bạn đã sử dụng.

Thay vì lộn xộn ..., bạn có thể sử dụng "giá trị được đặt trước" cho T1 để cho biết "không có", như void. Trong trường hợp đó, bạn có thể sử dụng thủ thuật này với các nhà xây dựng:

template<typename... Ts, typename=typename std::enable_if< ((sizeof...(Ts)==0) == (std::is_same<T1, void>::value)) && (sizeof...(Ts)<2) >::type > 
    foo_impl(T0 t0, Ts&&... ts): 
    m_t0(t0), foo_augment<Ts...>(std::forward<Ts>(ts)...) 
    {}; 

nơi các nhà xây dựng là variardic, nhưng SFINAE có nghĩa là các gói Ts... tham số phải là 0 yếu tố khi và chỉ khi T1void, và phải là 1 yếu tố khi và chỉ khi T1 không phải là void.

(Mã chưa được biên soạn nhưng thiết kế cơ bản phải rõ ràng).

2

Nhìn kỹ mã của bạn, bạn đang thực hiện lại std::tuple. Trao đổi các phương thức t1t2 cho chức năng miễn phí std::get<N> và bạn có mọi thứ (và có thể nhiều hơn) std::tuple cung cấp cho bạn. Để thuận tiện, nếu nó trở thành một phương pháp, xem xét việc này:

template<typename... Ts> 
struct foo { 
    typedef std::tuple<Ts...> Tup; 
    Tup m_ts; 
    foo(Ts... ts) : m_ts{ts...} {} //! 

    template <unsigned N> 
    std::tuple_element<N, Tup> t() { return std::get<N>(Tup); } 
}; 

Đối với //!: Tất nhiên bạn có thể làm điều đó constructor một (variadic) mẫu và chỉ chuyển tiếp các đối số cho các tuple. Oh, và accessor có thể/nên được quá tải cho const và nonconst và trở về appropiate tài liệu tham khảo để các yếu tố tuple ...

Nhưng nghiêm túc, nó không có giá trị mồ hôi. Chỉ cần sử dụng đồng bằng std::tuple. Ngoại trừ tất nhiên bạn đã đơn giản hóa vấn đề và bạn đang làm điều gì đó khác với bạn đã nói với chúng tôi.

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