2016-05-27 12 views
14

Şablon parametresi türüne bağlı olarak şablon parametre türüyle bir sınıf oluşturmanın bir yolunu arıyorum. numara 500 alacak, constexpr fonksiyon type_from_size()Bir tip döndüren bir constexpr işlevi nasıl oluşturabilirim (şablon parametresinde kullanılacak)

template<size_t n> 
constexpr auto type_from_size() { 
    if(n < 256) { 
     return uint8_t; 
    } else { 
     return uint16_t; 
    } 
} 

template<size_t n> 
class X { 
    type_from_size<n>() t; 
} 

X<500> x; 
x.t = 500; 

Yani, yukarıdaki kodda ve türünü uint16_t döneceğini ve bu olacaktır:

Ne yapmaya çalışıyorum böyle bir şeydir üye X.t'un türü.

Bunun açıkçası korkunç bir kod olduğunu biliyorum, ancak bu şablonlar kullanılarak mümkün mü?

+1

Sanırım türünü iade edemezsiniz. Ancak "typedef" ile şablon sınıfları ile "tipini döndürebilirsiniz". – PcAF

+0

Boost.Hana bunun için bir şeye sahip olmalı, ancak sonucun daha yararlı olup olmadığı, onunla ne yaptığınıza bağlı. Bu durumda, normal bir şablon muhtemelen sizin için daha yararlıdır. – chris

cevap

14

Bir işlev bir tür döndüremiyor. Bir şablon kullanmalısın.

Sadece iki tür arasında seçim yapmak için built-in std::conditional yeterlidir. İkiden fazla değerleri desteklemek gerekiyorsa

#include <type_traits> 
#include <cstdint> 

template <size_t n> 
using type_from_size = typename std::conditional<(n < 256), uint8_t, uint16_t>::type; 
//^if `n < 256`, the ::type member will be typedef'ed to `uint8_t`. 
//     otherwise, it will alias to `uint16_t`. 
// we then give a convenient name to it with `using`. 

template <size_t n> 
struct X { 
    type_from_size<n> t; 
    //^use the template 
}; 

, sen GÖZLERİM

template <size_t n> 
using type_from_size = 
    typename std::conditional<(n <= 0xff), uint8_t, 
     typename std::conditional<(n <= 0xffff), uint16_t, 
      typename std::conditional<(n <= 0xffffffff), uint32_t, 
       uint64_t 
      >::type 
     >::type 
    >::type; 
OH birlikte bir if/else if/else zincir gibi çoklu conditional değiştirebilirsiniz ancak

Ayrıca ihtisas kullanabilirsiniz std::enable_if (SFINAE) ile birlikte daha "düşük seviye" yapmak için birlikte:

template <size_t n, typename = void> 
struct type_from_size_impl; 
// Declare a "size_t -> type" function. 
// - the `size_t n` is the input 
// - the `typename = void` is a placeholder 
// allowing us to insert the `std::enable_if` condition. 

template <size_t n> 
struct type_from_size_impl<n, typename std::enable_if<(n <= 0xff)>::type> { 
    using type = uint8_t; 
}; 
// We add a partial specialization 
// - in `std::enable_if<c>::type`, if `c` is true, `::type` will be typedef'ed to `void` 
// - otherwise, `::type` will not be defined. 
// - if `::type` is not defined, substitution failed, 
// meaning we will not select this specialization 

template <size_t n> 
struct type_from_size_impl<n, typename std::enable_if<(n > 0xff && n <= 0xffff)>::type> { 
    using type = uint16_t; 
}; 

template <size_t n> 
struct type_from_size_impl<n, typename std::enable_if<(n > 0xffff && n <= 0xffffffff)>::type> { 
    using type = uint32_t; 
}; 

template <size_t n> 
struct type_from_size_impl<n, typename std::enable_if<(n > 0xffffffff)>::type> { 
    using type = uint64_t; 
}; 

template <size_t n> 
using type_from_size = typename type_from_size_impl<n>::type; 
// Here we want to find a specialization of `type_from_size_impl<n>` 
// All 4 specializations will be tried. 
// If only one specialization works, we will use that one 
// (Which is why we need to ensure the ranges are not overlapping 
// otherwise the compiler will complain) 
// Then we take the `::type` out the complete this "type-level function". 
+1

Teşekkürler! Büyük tepki. Son seçeneği kullanarak bittim, ancak std :: koşullu için +1 farkında olmadım. –

+1

İşlevler, türlerin çıkarılabildiği türden veya daha fazla etiket türünden döndürebilir. – Yakk

+0

@Yakk Boost kütüphanesi 'Boost.Hana', bu tür" tip programlamanın "harika bir örneğidir. – KABoissonneault

5

Daha fazla gidelim. Bir seçici ile başlayın: Bir tür döndüren basamaklı bir dizi aşırı yüklenme oluşturun. Ardından, bir tür döndüren bir dizi aşırı yüklenme oluşturun. seçim en küçük tip tüm ara tamsayı türleri için iki taraflı aralıkları yazmak zorunda kalmadan, ilk seçilen edilmesini sağlar:

template <class T> struct tag { using type = T; } 
template <size_t N> using size_t_ = std::integral_constant<size_t, N>; 

template <size_t N, class = std::enable_if_t<(N < (1ULL << 8))>> 
constexpr tag<uint8_t> tag_from_size(size_t_<N>, choice<0>) { return {}; } 

template <size_t N, class = std::enable_if_t<(N < (1ULL << 16))>> 
constexpr tag<uint16_t> tag_from_size(size_t_<N>, choice<1>) { return {}; 

template <size_t N, class = std::enable_if_t<(N < (1ULL << 32))>> 
constexpr tag<uint32_t> tag_from_size(size_t_<N>, choice<2>) { return {}; } 

template <size_t N> 
constexpr tag<uint64_t> tag_from_size(size_t_<N>, otherwise) { return {}; } 

Ve sonra gönderir üst düzey birini yazabilirsiniz:

template <size_t N> 
using type_from_size_t = typename decltype(tag_from_size(size_t_<N>{}, choice<0>{}))::type; 

Ve kullanmak: Kesinlikle

template <size_t N> 
class X { 
    type_from_size_t<N> t; 
}; 
+0

Neden 'tag' bırakmıyor ve işlevleri uygulamadan atlamıyorsunuz? Her şeyden sonra değerlendirilmemiş bir bağlamdalar. – Quentin

+0

@Quentin Bu her zaman işe yarıyor. İade edilemez türler üretmeye ihtiyaç duyabilecek diğer işlevler için istisnalara sahip olmanıza gerek yok - sadece tutarlılık için bir etiket döndürün. – Barry

+0

Anlam verir. - – Quentin

4

. İşte bunu yapmanın daha esnek bir yolu, üst üste gelmedikleri sürece istediğiniz kadar aralık ekleyebilirsiniz.

template <std::size_t N, class = void> 
struct TypeForSize_; 

template <std::size_t N> 
struct TypeForSize_<N, std::enable_if_t< 
    (N <= 255) 
>> { using type = std::uint8_t; }; 

template <std::size_t N> 
struct TypeForSize_<N, std::enable_if_t< 
    (N > 255 && N <= 65535) 
>> { using type = std::uint16_t; }; 

template <std::size_t N> 
using TypeForSize = typename TypeForSize_<N>::type; 

Hiçbir tür tanımlanmamış bir boyut kullanmak, derleme zamanı hatasıyla sonuçlanır.

3

İlk önce static_if<bool>(A, B) yazın.

Sonraki, template<class T> struct tag_type{using type=T;}; ve destek kodunu yazın.

template<size_t n> 
constexpr auto type_from_size() { 
    return static_if< (n<256) >( 
    tag<std::uint8_t>, 
    tag<std::uint16_t> 
); 
} 

şimdi n değerine bağlı olarak farklı bir etiket türü döndürür.

template<size_t n> 
class X { 
    typename decltype(type_from_size<n>())::type t; 
} 

veya hızlı takma yazma:

kullanmak için

İşte
template<size_t n> type_from_size_t = typename decltype(type_from_size<n>())::type; 

tag_type ve static_if kodudur:

template<class T>struct tag_type{using type=T;}; 
template<class T>constexpr tag_type<T> tag{}; 

template<bool b>using bool_t=std::integral_constant<bool, b>; 

template<class T, class F> 
constexpr T static_if(bool_t<true>, T t, F f) { 
    return t; 
} 
template<class T, class F> 
constexpr F static_if(bool_t<false>, T t, F f) { 
    return f; 
} 
template<bool b, class T, class F> 
constexpr auto static_if(T t, F f) { 
    return static_if(bool_t<b>, t, f); 
} 
template<bool b, class T> 
constexpr auto static_if(T t) { 
    return static_if<b>(t, [](auto&&...){}); 
} 

ve bitti.

Not static_case'u da yapabiliriz. :)

İlgili konular