Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to specialize a function template by an array type?

Let's say, we need a function template which should return an integer depending on a type:

template<typename T>
int get_type();

Further, we do specialize it with couple of types:

template<>
int get_type<int>()
{
  return TYPE_INT;
}

// And so on for other types...

And this works well, but not for array types. I can do the following:

template<>
int get_type<char[]>()
{
  return TYPE_STRING;
}

and compiler "agrees" with this, but linker does not. Because the type char[] differs against, for example, the char[5].

Is there any way to implement this function template without function parameters? I.e., I know that we can do something like this:

template<typename T>
int get_type(const T&);

But, actually the function parameter is not needed (used) here.

EDIT:

I use the C++ 11.

like image 538
Serge Roussak Avatar asked Apr 16 '20 08:04

Serge Roussak


People also ask

When we specialize a function template it is called?

To do so, we can use a function template specialization (sometimes called a full or explicit function template specialization) to create a specialized version of the print() function for type double.

What is the specialty of a template function give example?

Template in C++is a feature. We write code once and use it for any data type including user defined data types. For example, sort() can be written and used to sort any data type items. A class stack can be created that can be used as a stack of any data type.

How will you restrict the template for a specific datatype?

There are ways to restrict the types you can use inside a template you write by using specific typedefs inside your template. This will ensure that the compilation of the template specialisation for a type that does not include that particular typedef will fail, so you can selectively support/not support certain types.

How do you define a function template?

Defining a Function Template In the above code, T is a template argument that accepts different data types ( int , float , etc.), and typename is a keyword. When an argument of a data type is passed to functionName() , the compiler generates a new version of functionName() for the given data type.


4 Answers

You cannot partial specialize template functions (but you can for template classes)

Another approach is tag dispatching with overloads, instead of specialization:

template <typename> struct Tag{};

constexpr int get_type(Tag<int>) { return TYPE_INT; }

template <std::size_t N>
constexpr int get_type(Tag<char[N]>) { return TYPE_STRING; }

template <typename T>
constexpr int get_type() { return get_type(Tag<T>{}); }
like image 142
Jarod42 Avatar answered Oct 17 '22 14:10

Jarod42


You need a partial specialisation to account for variable array lengths, and C++ does not allow partially specialised function templates. The canonical solution is to (partially) specialise a class template with a (static) member (function), and dispatch to that from within your unspecialised function template:

namespace detail {
    template <typename T>
    struct get_type;

    template <>
    struct get_type<int> {
        static constexpr int value = TYPE_INT;
    };

    template <>
    struct get_type<char> {
        static constexpr int value = TYPE_CHAR;
    };

    template <typename T, std::size_t N>
    struct get_type<T[N]> {
        static constexpr int value = get_type<T>::value | TYPE_ARRAY;
    };

    template <std::size_t N>
    struct get_type<char[N]> {
        static constexpr int value = TYPE_STRING;
    };
} // namespace detail

template<typename T>
constexpr int get_type() {
    return detail::get_type<T>::value;
}
like image 8
Konrad Rudolph Avatar answered Oct 17 '22 14:10

Konrad Rudolph


You can't partially specialize function for array with size. But you can do it with class.

template<typename T>
class type
{
    static int get_type();
};

template<>
struct type<int>
{
    static int get_type() { return 1; }
};

template<size_t SZ>
struct type<char[SZ]>
{
    static int get_type() { return 2; }
};

template<typename T>
int get_type() { return type<T>::get_type(); }

int main()
{
    std::cout << get_type<char[3]>() << std::endl;
    return 0;
}

example

like image 6
ForEveR Avatar answered Oct 17 '22 13:10

ForEveR


Konrad already described the best approach in my opinion.

Here is another approach with overloads and specialization powered by SFINAE

// overload 1, for non-array types
template<typename T>
std::enable_if_t<!std::is_array_v<T>, int> get_type();

// specialization of overload 1 for int
template <>
auto get_type<int>() -> int {
    return 1;
}

// overload 2, for array types
template <typename T>
auto get_type() -> std::enable_if_t<std::is_array_v<T>, int> {
    return 3;
}

like image 3
sparik Avatar answered Oct 17 '22 14:10

sparik