10

Tôi muốn một mẫu để chọn từ hai loại dựa trên một số điều kiện. Ví dụ.Tính chất kiểu C++ để chọn giữa T1 và T2

struct Base {}; 

template <typename T1, typename T2> 
struct test 
{ 
    // e.g. here it should select T1/T2 that is_base_of<Base> 
    typename select_base<T1, T2>::type m_ValueOfBaseType; 
}; 

Tất nhiên để chuyển điều kiện đến select_base (để làm cho nó chung) sẽ hữu ích, nhưng giải pháp mã hóa cứng cũng dễ dàng và tốt hơn.

Dưới đây là một giải pháp mẫu mà tôi đã cố gắng nhưng nó luôn luôn chọn T1: http://ideone.com/EnVT8

Câu hỏi đặt ra là làm thế nào để thực hiện các mẫu select_base.

+0

Câu hỏi của bạn là gì? – Nawaz

+0

Câu hỏi của bạn không rõ ràng. Bạn muốn chọn loại cơ sở trong hai loại 'T1' hoặc' T2', ví dụ: nếu 'T1' xuất phát từ' T2', thì kiểu đầu ra phải là 'T2'. Hoặc bạn muốn chọn loại xuất phát từ loại thứ ba, nói 'cơ sở'? – Nawaz

+0

@Nawaz: từ ideone được liên kết, tôi sẽ nói 'Base' là cố định và anh ta muốn chọn bất kỳ' T1' hoặc 'T2' nào xuất phát từ nó. –

Trả lời

6

C++ 14 (trở đi):

template <typename T, typename U> 
struct select_base: 
    std::conditional_t<std::is_base_of<T, Base>::value, T, U> {}; 

Trong bối cảnh đó, bạn thay vì có thể sử dụng này:

template<typename T, typename U> 
using select_base = std::conditional_t<std::is_base_of_v<T,Base>, T, U>; 

Sự khác biệt giữa hai cách tiếp cận này có thể được quan sát thấy khi bạn sử dụng chúng. Ví dụ, trong trường hợp đầu tiên nếu bạn phải sử dụng ::type trong khi trong lần thứ hai, bạn không. Và nếu bất kỳ loại phụ thuộc nào liên quan đến việc sử dụng phương pháp tiếp cận đầu tiên, bạn cũng phải sử dụng typename để hỗ trợ trình biên dịch. Cách tiếp cận thứ hai là miễn phí của tất cả các tiếng ồn như vậy và do đó vượt trội so với phần còn lại của các phương pháp tiếp cận trong câu trả lời này.

Ngoài ra, xin lưu ý rằng bạn cũng có thể viết cùng loại bí danh trong C++ 11.


C++ 11:

template <typename T, typename U> 
struct select_base: 
    std::conditional<std::is_base_of<T, Base>::value, T, U>::type {}; 
//    ^          ^~~~~~ 

C++ 98:

có điều kiện là đủ dễ dàng:

template <typename bool, typename T, typename U> 
struct conditional { typedef T type; }; 

template <typename T, typename U> 
struct conditional<false, T, U> { typedef U type; }; 

is_base_of hơi phức tạp hơn, việc triển khai có sẵn trong Tăng cường mà tôi sẽ không tái tạo ở đây.

Sau đó, xem C++ 11.

+0

Có, điều này là chung chung và hoạt động tốt, cảm ơn! Tôi hoàn toàn quên các tham số mẫu không kiểu. – queen3

+0

Mẫu 'if_' đã được chuẩn hóa thành' std :: conditional'. http://en.cppreference.com/w/cpp/types/conditional – NicholasM

+0

@NicholasM: Đã cập nhật câu trả lời. –

18

Nếu bạn sử dụng std::conditional thay vì if_ lớp mẫu như thực hiện bởi @Matthieu trong câu trả lời của mình, sau đó giải pháp của bạn sẽ làm giảm thế này:

template <typename T, typename U> 
struct select_base 
{ 
    typedef typename std::conditional<std::is_base_of<T, Base>::value, T, U>::type base_type; 
}; 

Hoặc đơn giản này:

template <typename T, typename U> 
struct select_base : std::conditional<std::is_base_of<T, Base>::value, T, U> {}; 

trông thậm chí còn tốt hơn.

Sự khác biệt giữa hai giải pháp này là trong dung dịch đầu tiên bạn đưa ra một lập trình thân thiện với tên cho loại lồng nhau, như tôi đã cho nó base_type, trong khi ở các giải pháp thứ hai loại lồng nhau chỉ là type mà trông không thân thiện với lập trình viên.

Lưu ý rằng trong cả hai trong những giải pháp trên, bạn đã sử dụng các loại lồng nhau là một trong hai select_base<T,U>::base_type (trong dung dịch đầu tiên) hoặc select_base<T,U>::type (trong dung dịch thứ hai — và vì lý do đó, nếu bạn đã sử dụng typename như bạn đã viết cho mình trong câu hỏi bản thân

Tuy nhiên, nếu bạn thay sử dụng mẫu bí danh, định nghĩa là:.

template<typename T, typename U> 
using base_type = typename std::conditional<std::is_base_of<T, Base>::value, T, U>::type; 

sau đó bạn có thể sử dụng base_type<T,U> mà không lồng nhau-type và typename as:

template <typename T1, typename T2> 
struct test 
{ 
    //typename select_base<T1, T2>::type m_ValueOfBaseType; //ugly! 

    base_type<T1, T2> m_ValueOfBaseType; //better 
}; 

Hy vọng điều đó sẽ hữu ích.

+0

Ah cảm ơn, tôi không thể quấn quanh đầu của tôi, tên này chỉ là quá lạ: x (cũng, trừ khi bạn đang thiếu một 'typedef typename' ở đây). –

+0

@MatthieuM: Ohh quên phần đó. – Nawaz

+0

Vâng, cảm ơn lời khuyên, sau câu trả lời của Matthieu tôi chắc chắn tôi sẽ tìm thấy một cái gì đó tương tự trong type_traits, và tất nhiên nó ở đó. Tôi cho rằng tôi phải đọc các tài liệu trước, bởi vì việc tái phát minh bánh xe là vui nhưng không hiệu quả. – queen3

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