我有这样的代码:
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都相同的方法。我不想将一个复制粘贴代码换成另一个。
用于解释两个错误消息发生原因的奖励。我打破了什么规则。对我来说,...::type
或T
似乎没有取代void
。
答案 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>{});
}
};
这样可行,但你希望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{};
,结果将完全符合您的要求。
如果您想创建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;
}
};
或者,在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;
}
}