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?
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
}
// ...
}