2013-05-04 22 views
4

我有一个模板化的类,只能为标量类型(整数,浮点数等)实例化,我想要一个成员typedef始终是类型的签名变体。那就是:std :: make_signed接受浮点类型

unsigned int - >signed int
signed long long - >signed long long(已签署)
unsigned char - >signed char
float - >float
long double - >long double
etc ...

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

回答

8

一个简单的模板别名会做:

#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

+0

我更喜欢用'std :: common_type '来创建自己的'identity'类;} – 2013-05-04 19:21:11

+0

+1,'using'很方便,我需要最后更新我的编译器:) – jrok 2013-05-04 19:31:55

3

在我最初尝试使用std::conditional之后,我决定改用SFINAE。我使用std::enable_if到conditionaly启用浮点类型专业化:

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; 
}; 
+0

如果'make_signed'不专用于'T',那么这不会产生错误吗? (这就是'float'的情况)。 – mfontanini 2013-05-04 19:02:30

+0

@mfontanini我不好,现在不会。感谢您的高举。 – jrok 2013-05-04 19:05:00

+0

不客气:D。但是现在,您的条件将根据条件的结果生成'std :: make_signed '(应用':: type'确定)或'T'(例如可以是浮点数)。 – mfontanini 2013-05-04 19:07:47

0
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; 
} 
+0

这不是很便携,因为它可以' t支持任意支持的类型(例如,编译器可以支持'std :: uint512_t',客户端代码可以实例化我的类) – 2013-05-04 19:23:14

+0

@ LB--是的。但他只需要确定他使用的是什么。顺便说一句'std :: make_signed'支持那些? – stardust 2013-05-04 19:26:58

+0

是的,它必须是因为它是由编译器实现定义的。 – 2013-05-04 19:30:28

2

@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的回答是用别名模板来做的,这样更好。

+0

这可以工作,但它的缺点是你必须每次定义它。使用我的答案中的别名模板,您可以只使用'SignedT = typename try_make_signed :: type',就完成了。 – 2013-05-04 19:41:12

+0

这个答案中的类很容易被重构为一个类型特征类,所以我不知道你的意思是'缺点' - 如果我们的答案是一样的话,对吗? – 2013-05-04 19:44:36

+0

够公平的,你可以修改它来创建一个类型特征,是的 - 如果使用别名模板完成,就更好了,就像在我的回答中一样 – 2013-05-04 19:47:20