2016-01-06 18 views
7

Tôi có một loại như thế này:có điều kiện định nghĩa kiểu bí danh

template<typename T> 
struct wrapper 
{ 
    using foo = typename T::foo; 
    using bar = typename T::bar; 
    using baz = typename T::baz; 
    // More of those... 
}; 

Tôi muốn foo, bar, baz và tương đương loại bí danh được xác định khi và chỉ khi các loại tương đương tồn tại trong T. Các giải pháp sử dụng std::conditional cho phép thay thế bằng một thứ khác khi nó không tồn tại, nhưng tôi không biết làm thế nào để đảm bảo rằng nó không tồn tại chút nào khi kiểu tương ứng không tồn tại trong kiểu mẫu. Đoạn mã trên gây ra lỗi khi wrapper<T> được khởi tạo nếu T không xác định một trong các bí danh loại.

Tôi không thể thực hiện wrapper được kế thừa từ Twrapper không được phép làm mọi thứ T có thể thực hiện. Ngoài ra, bằng cách sử dụng một phần chuyên môn hóa sẽ dẫn đến một số loại vụ nổ mũ và sẽ nhanh chóng trở thành không thể duy trì. Tôi có thể tạo foo, bar ... bí danh loại mẫu để chèn một thông số mẫu mặc định nhưng sau đó người dùng sẽ phải viết wrapper<T>::foo<>, wrapper<T>::bar<> thay vì wrapper<T>::foo, wrapper<T>::bar, v.v ... và tôi không muốn điều đó.

Có cách nào đơn giản nhưng dễ bảo trì để xác định bí danh loại như vậy chỉ khi bí danh loại tương ứng tồn tại trong T?

Trả lời

8

Bạn có thể xác định check_foo, check_barcheck_baz đặc điểm mà chỉ có các loại nếu nó tồn tại, sau đó kế thừa từ tất cả trong số họ trong wrapper:

template <typename T, typename=void> 
struct check_foo{}; 

template <typename T> 
struct check_foo<T, void_t<typename T::foo>> { 
    using foo = typename T::foo; 
}; 

// ditto for bar, baz, etc. 

template <typename T> 
struct wrapper : 
    check_foo<T>, 
    check_bar<T>, 
    check_baz<T> 
{ }; 

Đó là một struct thêm mỗi loại, nhưng chắc chắn thích hợp hơn để các phiên bản số mũ bạn đã đề cập. Bạn thậm chí có thể làm cho nó một macro nếu bạn là phù hợp ngoan cố:

#define DEFINE_CHECKER(NAME) \ 
    template <typename T, typename=void> struct check_##NAME{}; \ 
    template <typename T> struct check_##NAME<T,void_t<typename T::NAME>> \ 
    { using NAME = typename T::NAME; }; 

DEFINE_CHECKER(foo) 
DEFINE_CHECKER(bar) 
DEFINE_CHECKER(baz) 

Horrible, tôi biết, nhưng tôi nghĩ rằng bạn có thể cần phải trả giá rằng nếu bạn thực sự muốn wrapper<T>::bar hơn wrapper<T>::bar<>. Nếu bạn sử dụng phiên bản macro, việc thêm loại mới sẽ có nghĩa là chỉ DEFINE_CHECKER(newname) mới và thêm check_newname<T> vào danh sách thừa kế trình bao bọc. Có thể tệ hơn.

Live Demo

+1

Đó là giải pháp mà tôi có trong đầu và tôi đã sẵn sàng dùng thử. Nó không thực sự đẹp, nhưng vẫn duy trì được nhiều hơn các giải pháp khác mà không để lộ những thứ vô dụng cho người dùng. Cảm ơn :) – Morwenn

+0

Dang. Tôi đã hy vọng rằng sẽ có một giải pháp đẹp hơn thế này. – Justin

5

Lưu ý rằng câu trả lời bằng void_t bởi @TartanLlama là tốt vì nó là. Tuy nhiên, trong C++ 17 có nhiều khả năng sẽ có một vài người trợ giúp Thư viện chuẩn như is_detected_v sẽ thực hiện các cuộc gọi đến void_t dưới mui xe.

#include <experimental/type_traits> 

// helpers to reduce boilerplate 
template<class Tag> 
struct empty_base {}; 

template<template<class> class Holder, template<class> class Op, class Arg> 
using inject_or_t = std::conditional_t 
< 
    std::experimental::is_detected_v<Op, Arg>, 
    Holder<Arg>, 
    empty_base<Op<Arg>> 
>; 

// add detector + holder for every conditional nested type 

template<class T> 
using foo_t = typename T::foo; 

template<class T> 
struct foo_holder { using foo = foo_t<T>; }; 

template<class T> 
using bar_t = typename T::bar; 

template<class T> 
struct bar_holder { using bar = bar_t<T>; }; 

template<class T> 
using baz_t = typename T::baz; 

template<class T> 
struct baz_holder { using baz = baz_t<T>; }; 

// wrapper is now simply: 

template<class T> 
struct wrapper 
: inject_or_t<foo_holder, foo_t, T> 
, inject_or_t<bar_holder, bar_t, T> 
, inject_or_t<baz_holder, baz_t, T> 
{}; 

struct Test 
{ 
    using foo = int; 
    using bar = int; 
    using baz = int; 
}; 

int main() 
{ 
    static_assert(!std::experimental::is_detected_v<foo_t, wrapper<int>>); 
    static_assert(!std::experimental::is_detected_v<bar_t, wrapper<int>>); 
    static_assert(!std::experimental::is_detected_v<baz_t, wrapper<int>>); 

    static_assert(std::experimental::is_detected_v<foo_t, wrapper<Test>>); 
    static_assert(std::experimental::is_detected_v<bar_t, wrapper<Test>>); 
    static_assert(std::experimental::is_detected_v<baz_t, wrapper<Test>>); 
} 

Live Example Lưu ý rằng ông là một trong những ví dụ rất hiếm nơi libstdC++ 6.0 SVN thân thể (hiện tại!) Làm điều gì đó mà libC++ 3.9 SVN thân cây không thể.

Điều này yêu cầu thêm bí danh máy dò và cấu trúc chủ cho từng loại được tiêm, và loại bỏ hoàn toàn nhu cầu về trình bao bọc macro.

+0

Giải pháp thực sự thanh lịch!Tất nhiên người ta có thể làm điều này trong C++ 14 bằng cách thực hiện những người trợ giúp thích hợp, hoặc blatantly ăn cắp chúng, như [this] (http://coliru.stacked-crooked.com/a/cee230048c1a807c). – TartanLlama

+0

Ồ, bây giờ đó là một giải pháp thú vị: o – Morwenn

+0

@TemplateRex Mặc dù đây không phải là những gì @Morwenn yêu cầu, phải không? 'wrapper ' chỉ cố gắng kế thừa từ 'int' ba lần thay vì kế thừa các bí danh thành viên' foo', 'bar' và' baz'. – TartanLlama

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