2016-05-27 70 views
14

我正在寻找一些方法来基于模板参数编号创建具有模板参数类型的类。如何创建一个返回类型的constexpr函数(用于模板参数)

我想要做的是这样的:

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; 

所以,在上面的代码中,constexpr功能type_from_size()将收到的号码500和将返回类型uint16_t,这将是成员类型X.t

我知道这显然是可怕的代码,但是这可能使用模板?

+1

我认为你不能返回类型。但是,您可以通过'typedef's通过模板类“返回类型”。 – PcAF

+0

Boost.Hana应该为此做些事情,但结果是否更有用取决于您对它做了什么。在这种情况下,常规模板可能对您更有用。 – chris

回答

14

一个函数不能返回一个类型。你应该使用一个模板。

对于只有两种类型的选择,built-in std::conditional就足够了。

#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 
}; 

如果您需要支持两个以上的值,你可以改变多个conditional在一起,就像一个if/else if/else链,但OH MY EYES

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; 

您也可以使用专业化与std::enable_if(SFINAE)一起使其更“低级”:

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

谢谢!伟大的回应。我结束了使用最后一个选项,但为std :: conditional +1,我不知道。 –

+1

函数可以返回类型,或者说可以从中提取类型的类型的标签。 – Yakk

+0

@Yakk Boost库Boost.Hana是这种“类型编程”的一个很好的例子。 – KABoissonneault

5

让我们去矫枉过正。从选择器开始:

template <int I> struct choice : choice<I + 1> { }; 
template <> struct choice<10> { }; 

struct otherwise { otherwise(...) { } }; 

然后创建一个级联的一系列重载返回类型。选择确保了最小的类型将被首先选择,而无需编写双面范围的所有中间整数类型:

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 {}; } 

然后就可以写顶层一个调度:

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

并使用它:

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

为什么不放下'tag'并跳过实现这些功能?毕竟他们处于一个没有评估的环境中:) – Quentin

+0

@Quentin这总是有效。不需要为可能需要产生不可返回类型的其他类型的函数创建异常 - 只需始终返回一个标记以保持一致性。 – Barry

+0

有道理。 - – Quentin

4

肯定。这是一种更灵活的方式,只要不重叠,您可以根据需要添加任意数量的范围。

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; 

使用未定义类型的大小将导致编译时错误。

3

首先写static_if<bool>(A, B)

接下来,写template<class T> struct tag_type{using type=T;};和支持代码。

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

现在返回基于的n值不同的代码类型。

要使用:

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

或编写一个简单的别名:

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

这里是tag_typestatic_if代码:

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&&...){}); 
} 

和完成。我们也可以做static_case。 :)

相关问题