I’m working in C++11, creating a class template. I wish to optimize certain methods according to the type traits of the template parameter (i.e. float/integer, signed/unsigned etc.). I know this could be achieved by template specialization for each specific combination of traits, but that’s too unwieldy.
Instead, I’ve got something working using the partial template specialization of a dummy struct wrapper (because so far as I’m aware partial specialization only works with structs/classes).
Working example here:
#include<type_traits>
#include<iostream>
template<typename T>
struct A
{
A( T _x ) : x(_x) {}
template<typename U, bool foobar = std::is_signed<T>::value>
struct temp{};
template<typename U>
struct temp<U,true>
{
static constexpr U diff( A const &left, A const &right ) noexcept
{
std::cout << "Signed" << std::endl;
T d = left.x - right.x;
return d * d;
}
};
template<typename U>
struct temp<U,false>
{
static constexpr U diff( A const &left, A const &right ) noexcept
{
std::cout << "Unsigned" << std::endl;
T d = left.x < right.x ? right.x - left.x : left.x - right.x;
return d * d;
}
};
protected:
T x;
};
int main( int argc, char** argv )
{
// Unsigned
A<unsigned int> u1( 10 );
A<unsigned int> u2( 15 );
// Method call
std::cout << A<unsigned int>::temp<unsigned long>::diff( u1, u2 ) << std::endl;
// Signed
A<float> f1( -1.23f );
A<float> f2( 12.3f );
// Method call
std::cout << A<float>::temp<double>::diff( f1, f2 ) << std::endl;
}
Having to dereference the dummy struct is cumbersome from the template user’s point of view, so I’d particularly like to improve that, if possible.
My knowledge of C++ is far from encyclopedic, so I’m wondering if there are nicer solutions out there. Preferably C++11 compatible, but I’m curious to hear about alternatives in modern C++ too.
Obviously I have googled this, but it took all my search skills to get the above working.
Thanks in advance.
>Solution :
You are overcomplicating it. You can add template overloads to you class and constrain those overloads to a particular condition. For this you would use
template<typename T>
struct A
{
A( T _x ) : x(_x) {}
template<typename U, bool foobar = std::is_signed<T>::value>
struct temp{};
// only enabled if U or by default T is signed
template<typename U = T, typename std::enable_if<std::is_signed<U>::value, bool>::type = true>
static constexpr U diff( A const &left, A const &right ) noexcept
{
std::cout << "Signed" << std::endl;
T d = left.x - right.x;
return d * d;
}
// only enabled if U or by default T is unsigned
template<typename U = T, typename std::enable_if<!std::is_signed<U>::value, bool>::type = true>
static constexpr U diff( A const &left, A const &right ) noexcept
{
std::cout << "Unsigned" << std::endl;
T d = left.x < right.x ? right.x - left.x : left.x - right.x;
return d * d;
}
protected:
T x;
};
and then your driver becomes
int main()
{
// Unsigned
A<unsigned int> u1( 10 );
A<unsigned int> u2( 15 );
// Method call
std::cout << A<unsigned int>::diff( u1, u2 ) << std::endl;
// Signed
A<float> f1( -1.23f );
A<float> f2( 12.3f );
// Method call
std::cout << A<float>::diff( f1, f2 ) << std::endl;
}
as seen in this live example