如何一次专门化多种类型的方法

时间:2016-11-09 18:21:22

标签: c++ c++11 templates

我有这样的代码:

template< class T >
struct Value
{
    /* quite a lot of other functions which I do not want to specialize, too */
    void print( void );
};

template<> void Value<          short int >::print() { std::cout << "is integral" << std::endl; }
template<> void Value<                int >::print() { std::cout << "is integral" << std::endl; }
template<> void Value<           long int >::print() { std::cout << "is integral" << std::endl; }
template<> void Value< unsigned short int >::print() { std::cout << "is integral" << std::endl; }
template<> void Value< unsigned       int >::print() { std::cout << "is integral" << std::endl; }
template<> void Value< unsigned  long int >::print() { std::cout << "is integral" << std::endl; }

template<> void Value<       float >::print() { std::cout << "is floating point" << std::endl; }
template<> void Value<      double >::print() { std::cout << "is floating point" << std::endl; }
template<> void Value< long double >::print() { std::cout << "is floating point" << std::endl; }

template< class T > void Value<T>::print() { std::cout << "unsupported type" << std::endl; }

int main( void )
{
    Value< float  >().print();
    Value< double >().print();
    Value< short  >().print();
    Value<   char >().print();
}

输出:

is floating point
is floating point
is integral
unsupported type

我想更改此项以减少代码重复,尤其是因为代码主体比简单的std::cout长得多。为了说明我想要的方向,想到的最简单的想法是使用宏:

#define TMP(T) \
template<> void Value<T>::print() { std::cout << "is integral" << std::endl; }
TMP(          short int )
TMP(                int )
TMP(           long int )
TMP( unsigned short int )
TMP( unsigned       int )
TMP( unsigned  long int )
#undef TMP
#define TMP(T) \
template<> void Value<T>::print() { std::cout << "is floating point" << std::endl; }
TMP(       float )
TMP(      double )
TMP( long double )
#undef TMP

但是我希望使用C ++ 11模板魔术来实现它。我已经尝试使用std::enable_if,但我无法让它工作。例如。此

template< class T >
void Value<
    typename std::enable_if< std::is_integral<T>::value, T >::type
>::print( void )
{
    std::cout << "is integral" << std::endl;;
}

给了我

test.cpp:26:24: error: invalid use of incomplete type ‘struct Value<typename std::enable_if<std::is_integral<_Tp>::value, T>::type>’
 >::type >::print( void )
                        ^
test.cpp:14:8: error: declaration of ‘struct Value<typename std::enable_if<std::is_integral<_Tp>::value, T>::type>’
 struct Value

并在返回类型上使用std::enable_if

template< class T >
typename std::enable_if< std::is_integral<T>::value, void >::type
Value<T>::print( void )
{
    std::cout << "is integral" << std::endl;;
}

给了我:

test.cpp:41:1: error: prototype for ‘typename std::enable_if<std::is_integral<_Tp>::value, void>::type Value<T>::print()’ does not match any in class ‘Value<T>’
 Value<T>::print( void )
 ^
test.cpp:16:17: error: candidate is: static void Value<T>::print()
     static void print( void );

当然,已经有很多类似的问题:

但它们通常是简单的函数,而不是模板类的方法。此外,他们通常不会严格按照我的要求单独声明和定义。

This问题听起来非常相似,但它专门针对结构/类模板参数的方法,而不是另一个模板参数。

由于上述错误,我似乎无法将答案应用于我的具体问题。此外,我不想专门研究整个类,因为类本身共享许多对于所有类型T都相同的方法。我不想将一个复制粘贴代码换成另一个。

用于解释两个错误消息发生原因的奖励。我打破了什么规则。对我来说,...::typeT似乎没有取代void

2 个答案:

答案 0 :(得分:2)

要为您的结构允许SFINAE,您必须添加额外的参数:

template<class T, typename AlwaysVoid = void>
struct Value;

然后

template<class T>
struct Value<T, std::enable_if_t<std::is_integral<T>::value>>
{
    void print()
    {
        std::cout << "is integral" << std::endl;;
    }
};

答案 1 :(得分:2)

您可以使用代码分派。定义标签:

namespace tag {

struct integral{};
struct floating{};
struct error{};

}

从各种类型定义他们的映射:

namespace detail
{

template<typename T, typename = void>
struct get_tag : tag::error{};

template<typename T>
struct get_tag<T, std::enable_if_t<std::is_integral<T>::value>> : tag::integral{};

template<typename T>
struct get_tag<T, std::enable_if_t<std::is_floating_point<T>::value>> : tag::floating{};

}

为每个支持的标签定义您的功能:

void print(tag::error){
    std::cout << "unsupported type" << std::endl;
}
void print(tag::integral){
    std::cout << "is integral" << std::endl;
}
void print(tag::floating){
    std::cout << "is floating" << std::endl;
}

从静态方法转发到它时:

template< class T >
struct Value
{
    static void print( void ){
        ::print(detail::get_tag<T>{});
    }
};

demo

这样可行,但你希望char不被视为一个整数,所以你可能想要定义自己的特征,只有一个列出的特征(更详细地解释+ C ++ 11)版本here):

template<typename T, typename... Others>
struct is_any : std::disjunction<std::is_same<T, Others>...>
{
};

现在,您可以为积分标签编写以下内容

template<typename T>
struct get_tag<T, std::enable_if_t<
        is_any<T, short, int, long, unsigned short, unsigned, unsigned long>::value>> : tag::integral{};

,结果将完全符合您的要求。

demo

如果您想创建print成员函数,可以使它们成为模板,以避免不受支持的类型的编译错误:

template< class T >
struct Value
{
    static void print( void ){
        Value obj;
        obj.print(detail::get_tag<T>{});
    }
private:
    template<typename U = T>
    void print(tag::error){
        std::cout << "unsupported type" << std::endl;
    }
    template<typename U = T>
    void print(tag::integral){
        std::cout << "is integral" << std::endl;
    }
    template<typename U = T>
    void print(tag::floating){
        std::cout << "is floating" << std::endl;
    }

};

demo

或者,在C ++ 1z中,您可以使用constexpr if

static void print( void ){
    using this_tag = detail::get_tag<T>;
    if constexpr(std::is_base_of<tag::floating, this_tag>::value) {
        std::cout << "is floating" << std::endl;
    } else if constexpr(std::is_base_of<tag::integral, this_tag>::value) {
        std::cout << "is integral" << std::endl;
    } else {
        std::cout << "unsupported type" << std::endl;
    }
}

demo