【问题标题】:std::make_signed that accepts floating point types接受浮点类型的 std::make_signed
【发布时间】:2013-05-04 18:44:49
【问题描述】:

我有一个只能为标量类型(整数、浮点数等)实例化的模板类,我希望成员 typedef 始终是该类型的有符号变体。那就是:

unsigned int -> signed int
signed long long -> signed long long(已签名)
unsigned char -> signed char
float -> float
long double -> long double
等等……

不幸的是,std::make_signed 仅适用于整数类型,不适用于浮点类型。最简单的方法是什么?我正在寻找 using SignedT = ...; 形式的东西,作为模板参数 T 已经保证是标量的模板类的一部分。

【问题讨论】:

    标签: c++ templates c++11 typetraits


    【解决方案1】:

    一个简单的模板别名就可以了:

    #include <type_traits>
    
    template<typename T>
    struct identity { using type = T; };
    
    template<typename T>
    using try_make_signed =
        typename std::conditional<
            std::is_integral<T>::value,
            std::make_signed<T>,
            identity<T>
            >::type;
    

    你可以这样测试它:

    int main()
    {
        static_assert(::is_same<
            try_make_signed<unsigned int>::type, int
            >::value, "!");
    
        static_assert(std::is_same<
            try_make_signed<double>::type, double
            >::value, "!");
    }
    

    这是live example

    【讨论】:

    • 我更喜欢使用 std::common_type&lt;T&gt; 来创建自己的 identity 类;)
    • +1, using 太方便了,我需要最后更新我的编译器:)
    【解决方案2】:

    在我最初尝试使用std::conditional 有缺陷后,我决定改用 SFINAE。我正在使用std::enable_if 有条件地启用浮点类型的专业化:

    template<typename T, typename Enable = void>
    struct my_make_signed {
        typedef typename std::make_signed<T>::type type;
    };
    
    template<typename T>
    struct my_make_signed<T,
        typename std::enable_if<std::is_floating_point<T>::value>::type> {
        typedef T type;
    };
    

    【讨论】:

    • 如果make_signed 不是专门用于T,那不会产生错误吗? (floats 就是这种情况)。
    • @mfontanini 我的错,现在不会了。感谢您的提醒。
    • 不客气:D。但是现在您的条件将根据条件的结果产生std::make_signed&lt;T&gt;(应用::type 可以)或T(例如可以是浮点数)。
    • 是的,我正在修复它。我会删除,但不能,因为它已被接受。
    • @jrok 把它改成typename std::conditional&lt;std::is_floating_point&lt;T&gt;::value, std::common_type&lt;T&gt;, std::make_signed&lt;T&gt;&gt;::type::type
    【解决方案3】:

    @jrok 最初有可以工作的代码,他只需要做一个小的调整。这是工作代码:

    template<typename T>
    struct YourClass
    {
        using SignedT =
            typename std::conditional
            <
                std::is_floating_point<T>::value, //if floating point type
                std::common_type<T>,              //use it as-is
                std::make_signed<T>               //otherwise make sure it is signed
            >::type::type; //notice the double ::type
    };
    

    演示:http://ideone.com/Vw7o82

    如果需要多次使用此功能,也可以将上述结构修改为类型特征类。但是,@Andy Prowl 的答案是使用别名模板,这样更好。

    【讨论】:

    • 这行得通,但它的缺点是你必须每次都定义它。使用我的答案中的别名模板,您只需执行using SignedT = typename try_make_signed&lt;T&gt;::type,就完成了。
    • 这个答案中的类可以很容易地重构为一个类型特征类,所以我不知道你所说的“缺点”是什么意思——如果我们的答案是一样的,对吧?
    • 很公平,您可以对其进行修改以从中产生类型特征,是的 - 如果使用别名模板完成则更好,就像在我的回答中一样
    • 谢谢 :) 无论如何,std::common_type 的有趣用例(尽管我仍然相信 identity 有一个更好的名字)
    【解决方案4】:
    namespace mine {        
    
        template<typename T, bool b>
        struct make_signed__ {
            typedef T type;
        };
    
        template<typename T>
        struct make_signed__<T,false> {
            typedef typename std::make_signed<T>::type type;
        };
    
        template<typename T>
        struct make_signed {
            typedef typename make_signed__<T, std::is_floating_point<T>::value>::type type;
        };
    
    }
    
    int main () {
        std::cout << std::is_same<mine::make_signed<unsigned int>::type, int>::value;
        std::cout << std::is_same<mine::make_signed<long double>::type, long double>::value;
    }
    

    【讨论】:

    • 这不是很便携,因为它不能支持任意支持的类型(例如,编译器可以支持std::uint512_t,客户端代码可以用它来实例化我的类)
    • @LB——是的,这是真的。但他只需要定义他使用什么。顺便问一下std::make_signed 支持这些吗?
    • 是的,必须的,因为它是由编译器实现定义的。
    • @LB--已编辑。我认为它现在非常便携。
    • 没有宏也可以,那么 ;)
    猜你喜欢
    • 2019-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多