如何使用模板元编程在C ++ 17中将一种类型转换为另一种类型?

时间:2019-02-01 04:07:31

标签: c++ c++17 template-meta-programming

我需要在C ++中实现类型转换器,以将某种类型转换为另一种类型。例如,将int16_t转换为float,将int64_t转换为double。我已经使用模板专门化实现了一个:

template<class T>
struct TypeConverter
{

};
template<>
struct TypeConverter<int16_t>
{
       using type = float;
};

template<>
struct TypeConverter<int64_t>
{
       using type = double;
};

TEST(Exp, TypeConveter) {

    static_assert(std::is_same_v<TypeConverter<int16_t>::type, float>);
    static_assert(std::is_same_v<TypeConverter<int64_t>::type, double>);
}

使用C ++ 17 tmp功能是否有更好的方法(更紧凑)?

3 个答案:

答案 0 :(得分:3)

另一种简单方式(与C ++ 11兼容):

template<class T> struct Type {};

float type_convert(Type<int16_t>);
double type_convert(Type<int64_t>);

int main() {
    static_assert(std::is_same_v<decltype(type_convert(Type<int16_t>{})), float>);
    static_assert(std::is_same_v<decltype(type_convert(Type<int64_t>{})), double>);
}

此方法的优点在于,它使用基于参数的名称查找(ADL)查找相应的type_convert函数 declaration (无需 definition ) 。如果您需要处理用户定义的类型(UDT),则可以在声明它们的相同名称空间中为它们添加type_convert的相应重载(而不必打开traits命名空间来定义特征的另一个特殊化)您的UDT的课程模板)。例如:

namespace N { 
    struct MyType;
    long double type_convert(Type<MyType>);
} 

然后:

// type_convert is found by ADL.    
static_assert(std::is_same_v<decltype(type_convert(Type<N::MyType>{})), long double>);

答案 1 :(得分:1)

CE C ++ 17:https://gcc.godbolt.org/z/iY8Qoa

CE C ++ 11:https://gcc.godbolt.org/z/ApuxZj

用户代码

#include<TypeMap.h>
#include<cstdlib>
using TMap = TypeMap <
    std::pair<int16_t, float>,
    std::pair<int64_t, double>
>;

void foo() {
    static_assert(std::is_same_v<TMap::get<int16_t>, float>);
    static_assert(std::is_same_v<TMap::get<int64_t>, double>);
    static_assert(std::is_same_v<TMap::get<int>, NotFound>);
}

TypeMap.h

#include<type_traits>
#include<utility>

struct NotFound;

template<class... TPs>
struct TypeMap {
    template<class T>
    using get = NotFound;
};

template<class TP1, class... TPs>
struct TypeMap<TP1, TPs...> {
    template<class T>
    using get = std::conditional_t< std::is_same_v<T, typename TP1::first_type>
            , typename TP1::second_type
            , typename TypeMap<TPs...>::template get<T> >;
};

答案 2 :(得分:0)

我可以想像的为类型转换器注册类型的最综合的方法是在std::tuple(或类似的东西)中列出几个类型的列表。

例如:如果要将std::int16_t转换为float,将std::int32_t转换为double,将std::int64_t转换为long double,则可以{ {1}}定义几种类型

using

现在,给出以下结构和声明的函数

using list1 = std::tuple<std::int16_t, std::int32_t, std::int64_t>;
using list2 = std::tuple<float, double, long double>;

template <typename, typename, typename> struct foo { using type = std::tuple<>; }; template <typename T1, typename T2> struct foo<T1, T1, T2> { using type = std::tuple<T2>; }; template <typename T, typename ... Ts1, typename ... Ts2> constexpr auto bar (std::tuple<Ts1...>, std::tuple<Ts2...>) -> decltype( std::tuple_cat( std::declval<typename foo<T, Ts1, Ts2>::type>()...) ); 成为

TypeConverter

但是我想两个不同的template <typename T> using TypeConverter = std::tuple_element_t<0u, decltype(bar<T>(std::declval<list1>(), std::declval<list2>()))>; 中的几个列表是综合的,但难以理解和维护。

因此,我基于单个类型对列表提出了一种不太综合(但更易于理解和可维护)的方法

std::tuple

现在using list = std::tuple<std::pair<std::int16_t, float>, std::pair<std::int32_t, double>, std::pair<std::int64_t, long double>>; 并声明函数成为

struct

template <typename, typename> struct foo { using type = std::tuple<>; }; template <typename T1, typename T2> struct foo<T1, std::pair<T1, T2>> { using type = std::tuple<T2>; }; template <typename T, typename ... Ts> constexpr auto bar (std::tuple<Ts...>) -> decltype( std::tuple_cat( std::declval<typename foo<T, Ts>::type>()...) );

TypeConverter

以下是具有两种解决方案的完整的C ++ 17示例(您可以启用第一个或第二个更改template <typename T> using TypeConverter = std::tuple_element_t<0u, decltype(bar<T>(std::declval<list>()))>;

#if 0