Martin Stolpe
Martin Stolpe

Reputation: 185

Specialized templates for pointer and c-style array variables

I have the following code which works, when I compile the code with C++11 enabled. Is it also possible to write the specializations such that it will work with a C++98 compiler?

#include <iostream>
#include <type_traits>
#include <cstdint>

template<typename T, typename std::enable_if_t<!std::is_pointer<T>::value, int> = 0>
void CheckSize(T const)
{
    cout << "sizeof(data): " << sizeof(T) << endl;
}

template<typename T, typename std::enable_if_t<std::is_pointer<T>::value, int> = 0>
void CheckSize(T const)
{
    cout << "sizeof(data) (pointer): " << sizeof(std::remove_pointer<T>) << endl;
}

template<typename T, size_t N>
void CheckSize(T const (&)[N])
{
    cout << "sizeof(data) (array): " << sizeof(T) * N << endl;
}

int main()
{
    uint8_t bufferNumber{0};
    CheckSize(bufferNumber);
    uint8_t bufferArray[] = {1,2,3,4,5,6};
    CheckSize(bufferArray);
    uint8_t *bufferPointer{nullptr};
    CheckSize(bufferPointer);

    return 0;
}

I also don't understand why the compiler can't apply the specialization when writing:

template<typename T>
void CheckSize(T const)
{
    cout << "sizeof(data): " << sizeof(T) << endl;
}

template<typename T>
void CheckSize(T const*)
{
    cout << "sizeof(data) (pointer): " << sizeof(T) << endl;
}

MSVC2015 will print an error message that the function call is ambigious for the overloaded function for the bufferArray variable and MinGW will use the CheckSize(T const) function for the bufferPointer variable.

Upvotes: 2

Views: 390

Answers (2)

Miles Budnek
Miles Budnek

Reputation: 30569

As mentioned in the comments, enable_if and the type traits you're using are implementable using C++98. Boost provides implementations, and I would recommend using them if you're already using boost, but they're fairly simple to implement if you're not using boost:

template <bool b, typename T>
struct enable_if;

template <typename T>
struct enable_if<true, T>
{
    typedef T type;
};

template <typename T>
struct is_pointer
{
    const static bool value = false;
};

template <typename T>
struct is_pointer<T*>
{
    const static bool value = true;
};

template <typename T>
struct remove_pointer
{
    typedef T type;
};

template <typename T>
struct remove_pointer<T*>
{
    typedef T type;
};

template<typename T>
typename enable_if<!is_pointer<T>::value, void>::type
CheckSize(T const)
{
    std::cout << "sizeof(data): " << sizeof(T) << std::endl;
}

template<typename T>
typename enable_if<is_pointer<T>::value, void>::type
CheckSize(T const)
{
    std::cout << "sizeof(data) (pointer): " << sizeof(typename remove_pointer<T>::type) << std::endl;
}

template<typename T, size_t N>
void CheckSize(T const (&)[N])
{
    std::cout << "sizeof(data) (array): " << sizeof(T) * N << std::endl;
}

Live Demo

Alternatively, you could use partial specialization rather than SFINAE to select your overload. Since functions can't be partially specialized, you can partially specialize a helper class:

template<typename T>
struct CheckSizeHelper
{
    static void size() {
        std::cout << "sizeof(data): " << sizeof(T) << std::endl;
    }
};

template<typename T>
struct CheckSizeHelper<T*>
{
    static void size() {
        std::cout << "sizeof(data) (pointer): " << sizeof(T) << std::endl;
    }
};

template<typename T, size_t N>
struct CheckSizeHelper<T[N]>
{
    static void size() {
        std::cout << "sizeof(data) (array): " << sizeof(T) * N << std::endl;
    }
};

template<typename T>
void CheckSize(T const&) {
    CheckSizeHelper<T>::size();
}

Live Demo

Upvotes: 2

Dmitry Gordon
Dmitry Gordon

Reputation: 2324

As it was mentioned in the comments, one option is to use the boost::enable_if

Another option is to use partial template specialization for classes instead of function overloading:

#include <iostream>
#include <type_traits>
#include <cstdint>

using namespace std;

template<typename T>
struct SizeChecker
{
    static void CheckSize(T const)
    {
        cout << "sizeof(data): " << sizeof(T) << endl;
    }
};

template<typename T>
struct SizeChecker<T*>
{
    static void CheckSize(T* const)
    {
        cout << "sizeof(data) (pointer): " << sizeof(T*) << endl;
    }
};

template<typename T, size_t N>
struct SizeChecker<T[N]>
{
    static void CheckSize(const T(&)[N])
    {
        cout << "sizeof(data) (array): " << sizeof(T) * N << endl;
    }
};

template <typename T>
void CheckSize(const T& val)
{
    SizeChecker<T>::CheckSize(val);
}

int main()
{
    char bufferNumber{0};
    CheckSize(bufferNumber);
    char bufferArray[] = {1,2,3,4,5,6};
    CheckSize(bufferArray);
    char *bufferPointer{NULL};
    CheckSize(bufferPointer);

    return 0;
}

Upvotes: 1

Related Questions