Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use std::is_integral<> to select an implementation?

I'm trying to return an int64_t if std::is_integral<>::value is true.

Otherwise, I would like to call to_int64t() on the object.

My attempt below is failing because partial specialisation of function templates are not allowed.

CODE

#include <type_traits>
#include <cstdint>

template<class T,bool is_integral_type>
int64_t to_int64t( const T& t )
{
        return t;
}

template<class T>
int64_t to_int64t<T,std::is_integral<T>::value>( const T& t )
{
        return t;
}

template<class T>
int64_t to_int64t<T,!std::is_integral<T>::value>( const T& t )
{
        return t.to_int64t();
}

int main()
{
        int64_t i = 64;
        auto x = to_int64t( i );
}
like image 911
kfmfe04 Avatar asked Mar 24 '13 13:03

kfmfe04


People also ask

What is Is_integral?

std::is_integralTrait class that identifies whether T is an integral type. It inherits from integral_constant as being either true_type or false_type, depending on whether T is an integral type: fundamental integral types. bool. char.

When to use enable_ if?

std::enable_if can be used in many forms, including: as an additional function argument (not applicable to operator overloads) as a return type (not applicable to constructors and destructors) as a class template or function template parameter.

What is enable_ if in c++?

The enable_if family of templates is a set of tools to allow a function template or a class template specialization to include or exclude itself from a set of matching functions or specializations based on properties of its template arguments.


Video Answer


2 Answers

Function templates cannot be partially specialized and, in general, it is not a good idea to use function template specialization.

One way to achieve what you want is to use a technique called tag dispatching, which basically consists in providing a forwarder function that selects the right overload based on the value of an extra dummy argument:

#include <type_traits>
#include <cstdint>

template<class T>
int64_t to_int64t( const T& t, std::true_type )
{
    return t;
}

template<class T>
int64_t to_int64t( const T& t, std::false_type )
{
    return t.to_int64t();
}

template<class T>
int64_t to_int64t( const T& t )
{
    return to_int64t(t, std::is_integral<T>());
}

int main()
{
    int64_t i = 64;
    auto x = to_int64t( i );
}

Another possibility is to use the classical SFINAE technique based on std::enable_if. This is how that could look like (notice that, since C++11, default template arguments on function templates are allowed):

#include <type_traits>
#include <cstdint>

template<class T, typename std::enable_if<
    std::is_integral<T>::value>::type* = nullptr>
int64_t to_int64t( const T& t )
{
    return t;
}

template<class T, typename std::enable_if<
    !std::is_integral<T>::value>::type* = nullptr>
int64_t to_int64t( const T& t )
{
    return t.to_int64t();
}

int main()
{
    int64_t i = 64;
    auto x = to_int64t( i );
}

Yet another possibility, although more verbose, is to define helper class templates (which can be partially specialized) in a detail namespace and provide a global forwarder - I would not use this technique for this use case, but I am showing it because it might come handy in related design situations:

#include <type_traits>
#include <cstdint>

namespace detail
{
    template<class T, bool = std::is_integral<T>::value>
    struct helper { };

    template<class T>
    struct helper<T, true>
    {
        static int64_t to_int64t( const T& t )
        {
            return t;
        }
    };

    template<class T>
    struct helper<T, false>
    {
        static int64_t to_int64t( const T& t )
        {
            return t.to_int64t();
        }
    };
}

template<class T>
int64_t to_int64t( const T& t )
{
    return detail::helper<T>::to_int64t(t);
}

int main()
{
    int64_t i = 64;
    auto x = to_int64t( i );
}
like image 116
Andy Prowl Avatar answered Sep 28 '22 16:09

Andy Prowl


You can just use std::enable_if:

template<class T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
int64_t to_int64t( const T& t )
{
        return t;
}

template<class T, typename std::enable_if<!std::is_integral<T>::value, int>::type = 0>
int64_t to_int64t( const T& t )
{
        return t.to_int64t();
}
like image 36
Stephan Dollberg Avatar answered Sep 28 '22 16:09

Stephan Dollberg