Search code examples
c++templatesfunction-pointers

Assigning function to function pointer with template parameter


So I have these two functions:

bool intComp(int a, int b)
{
    return a > b;
}

bool stringComp(std::string a, std::string b)
{
    return strcmp(a.c_str(), b.c_str()) > 0;
}

And in my sort function I want to assign either the stringComp or intComp function:

template<typename T>
void sort(std::vector<T>& vector)
{
    bool (*compare)(T a, T b);

    if (typeid(T) == typeid(int))
    {
        compare = &intComp;
    }
    else if (typeid(T) == typeid(std::string))
    {
        compare = &stringComp;
    }
    ...
}

The assignment and sorting works fine when I remove the else if block with compare = &stringComp. But as soon as I try to assign functions with types other than int (for instance string) I get the following compiler error: '=': cannot convert from 'bool (__cdecl *)(std::string,std::string)' to 'bool (__cdecl *)(T,T)' What am I doing wrong? And why does the code work with int but not with other types? Do templates maybe work with integers under the hood similar to enums and that's why I can assign the intComp function without issues?


Solution

  • The problem is that all branches of a normal if need to be valid at compile time, but only one of the branches in yours is. If T is int, then compare = &stringComp is invalid. If T is std::string, then compare = &intComp is invalid.

    Instead, you need if constexpr, which was introduced in C++17 and does its comparison at compile time. It discards branches that it doesn't take as long as it's dependent on a template parameter, so it doesn't matter if they don't make sense for that type. For example:

    template <typename T>
    void sort(std::vector<T>& vector)
    {
        bool (*compare)(T a, T b);
    
        if constexpr (std::is_same_v<T, int>) {
            compare = &intComp;
        } else if constexpr (std::is_same_v<T, std::string>) {
            compare = &stringComp;
        } else {
            // Error
        }
    
        // ...
    }