代码之家  ›  专栏  ›  技术社区  ›  motam79

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

  •  2
  • motam79  · 技术社区  · 7 年前

    我需要在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 回复  |  直到 7 年前
        1
  •  3
  •   Maxim Egorushkin    7 年前

    另一种简单的方法(与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 公告 (否) 释义 类型转换 在声明它们的同一名称空间中(无需打开traits名称空间为UDT定义trait类模板的另一个专门化)。例如。:

    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>);
    
        2
  •  1
  •   balki    7 年前

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

    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> >;
    };
    
        3
  •  0
  •   max66    7 年前

    我能想象的为类型转换器注册类型的最综合的方法是在 std::tuple

    通过示例:如果要转换 std::int16_t float , std::int32_t double std::int64_t long double ,你可以 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 s是合成的,但很难理解和维护。

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

    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>()...) );
    

    类型转换器

    template <typename T>
    using TypeConverter
       = std::tuple_element_t<0u, decltype(bar<T>(std::declval<list>()))>;
    

    下面是使用这两种解决方案编译C++17的完整示例(您可以启用第一个或第二个更改 #if 0

    #include <tuple>
    #include <type_traits>
    
    #if 0
    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>()...) );
    
    using list1 = std::tuple<std::int16_t, std::int32_t, std::int64_t>;
    using list2 = std::tuple<float, double, long double>;
    
    template <typename T>
    using TypeConverter
       = std::tuple_element_t<0u, decltype(bar<T>(std::declval<list1>(),
                                                  std::declval<list2>()))>;
    #else
    
    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>()...) );
    
    using list = std::tuple<std::pair<std::int16_t, float>,
                            std::pair<std::int32_t, double>,
                            std::pair<std::int64_t, long double>>;
    
    template <typename T>
    using TypeConverter
       = std::tuple_element_t<0u, decltype(bar<T>(std::declval<list>()))>;
    #endif
    
    int main ()
     {
       static_assert( std::is_same_v<float, TypeConverter<std::int16_t>> );
       static_assert( std::is_same_v<double, TypeConverter<std::int32_t>> );
       static_assert( std::is_same_v<long double, TypeConverter<std::int64_t>> );
     }