5

Làm thế nào chúng ta có thể thực hiện một mẫu variadic rằng, do một loại T và một danh sách các loại E , E ... E N, xác định loại danh sách mà chuyển đổi từ T thành loại đó, theo độ phân giải quá tải, tốt nhất?variadic mẫu mà quyết định chuyển đổi tốt nhất

void phải là đầu ra nếu không có chuyển đổi tốt nhất - nói cách khác, khi có sự mơ hồ hoặc T không thể chuyển đổi thành bất kỳ loại nào trong danh sách.
Lưu ý rằng điều này ngụ ý rằng mẫu của chúng tôi phải thân thiện với SFINAE, tức là không phải là lỗi nghiêm trọng khi không có chuyển đổi tốt nhất nào tồn tại.

sau đây static_assert s nên thành công:

static_assert(std::is_same< best<int, long, short>,  void >{}, ""); 
static_assert(std::is_same< best<int, long, std::string>, long >{}, ""); 
static_assert(std::is_same< best<int>,     void >{}, ""); 

(Giả sử, vì đơn giản, đó best là một mẫu bí danh đề cập đến những mẫu thực tế)

Trường hợp này còn lại không xác định:

static_assert(std::is_same< best<int, int, int>, ???>{}, ""); 

Hoặc là void hoặc int phải được chấp nhận ở đây. (Nếu sau này được chọn sau đó chúng tôi vẫn có thể kiểm tra trong một mẫu wrapper cho dù loại kết quả được chứa hai lần trong danh sách, và nếu nó là, đầu ra void thay thế).

Trả lời

6

tôi cách tiếp cận hiện tốt nhất:

#include <type_traits> 

template <class T> using eval = typename T::type; 

template <class T> struct identity {using type = T;}; 

template <typename T, typename... E> 
class best_conversion 
{ 
    template <typename...> struct overloads {}; 

    template <typename U, typename... Rest> 
    struct overloads<U, Rest...> : 
     overloads<Rest...> 
    { 
     using overloads<Rest...>::call; 

     static identity<U> call(U); 
    }; 

    template <typename U> 
    struct overloads<U> 
    { 
     static identity<U> call(U); 
    }; 

    template <typename... E_> 
    static identity<eval<decltype(overloads<E_...>::call(std::declval<T>()))>> 
    best_conv(int); 

    template <typename...> 
    static identity<void> best_conv(...); 

public: 

    using type = eval<decltype(best_conv<E...>(0))>; 
}; 

template <typename... T> 
using best_conversion_t = eval<best_conversion<T...>>; 

Demo. Đối với trường hợp "không xác định" phía trên mẫu này, bạn sẽ cung cấp cho bạn int.

Ý tưởng cơ bản là đặt một loạt các hàm bị quá tải với một tham số vào các phạm vi khác nhau mà tra cứu tên sẽ nhìn vào, với tham số và kiểu trả về của từng quá tải tương ứng với một trong các loại trong danh sách của chúng tôi.
overloads thực hiện công việc này bằng cách đệ quy giới thiệu một khai báo call tại một thời điểm và điều chỉnh tất cả các mã được giới thiệu trước đây call s từ các chuyên môn cơ bản qua khai báo using. Bằng cách đó tất cả các call s là trong phạm vi khác nhau nhưng được coi là bằng nhau khi nói đến độ phân giải quá tải.

Sau đó áp dụng SFINAE trong mẫu chức năng best_conv để kiểm tra xem cuộc gọi đến call (bên trong overloads) có đúng dạng không: Nếu có, hãy trả về loại (theo định nghĩa kiểu tham số) của khai báo đã chọn và sử dụng nó như là kết quả của chúng tôi - nó sẽ là loại chúng tôi đang tìm kiếm.
Cũng cung cấp quá tải thứ hai là best_conv trả về void và có thể được chọn làm mặc định (khi SFINAE áp dụng trong lần nạp đầu tiên và đá nó ra khỏi tập hợp ứng cử viên).

Loại trả lại sử dụng identity<> để tránh phân rã loại khi làm việc với ví dụ: các kiểu con trỏ của mảng hoặc hàm.

+1

'(...)' là một chút bực mình: nhiều loại UB được chuyển thành. Làm cho nó 'T &&, ...' và thả nó? – Yakk

+0

@Yakk Nó chỉ đơn thuần là để có một 'int' (để làm cho nó một trận đấu tồi tệ hơn nếu cả hai quá tải' best_conv' ở lại trong tập ứng cử viên), đó là tốt. Tôi có thể làm cho nó 'long' nếu bạn muốn. – Columbo

+1

Ag, tôi thấy: not 'declval ()'. Thiết kế tôi có trong đầu của tôi đã buộc trường hợp 'void' cạnh tranh với các tình trạng quá tải khác trực tiếp hơn (với một' ... 'để đảm bảo nó đi cuối cùng), và tôi chiếu nó vào solutuon khác của bạn. Lấy làm tiếc! – Yakk

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