C++ How to write generic comparator function for int, double, string etc using templates

Hi I am trying to write a generic isLess compare function for int, double, null terminated character array etc. Below is my code for same, please help me understand how could we use this function for null terminated strings too.

#include<iostream>

using namespace std;

template<typename T>
bool isLess(T &x, T &y)
{
return x < y;
}

int main()
{
int a(10), b(20);
double c(2.0), d(3.0);
cout<<isLess<int>(a,b)<<endl;
cout<<isLess<double>(c,d)<<endl;
//For above types, generic comparator works fine

//but if we have to compare character represented string, how can we do this in our comparator
//assume charaters strings are compared the same way as strcmp.


//char *e = "str1";
//char *f = "str2";
//cout<<isLess<char*>(e,f)<<endl;

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<iostream>

using namespace std;



template<typename T>
bool isLess(T x, T y)
{
    return x < y;
}

template<>
bool isLess(char *x, char *y)
{
    return (strcmp(x, y) < 0);
}

int main()
{
int a(10), b(20);
double c(2.0), d(3.0);
cout<<isLess<int>(a,b)<<endl;
cout<<isLess<double>(c,d)<<endl;
//For above types, generic comparator works fine

//but if we have to compare character represented string, how can we do this in our comparator
//assume charaters strings are compared the same way as strcmp.


char *e = "str12";
char *f = "str1";
cout<<isLess<char*>(e, f)<<endl;

return 0;
} 


Using template specialization.
@SakurasouBusters : Thanks for your response.
I tried the exact same code as above but I am getting error


[Error] template-id 'isLess<>' for 'bool isLess(char*, char*)' does not match any template declaration at line n0. 14 in the above code :(

Even if I remove char * from line no. 33, the error persists.
Last edited on
Did you forget to #include the <cstring> header file required for the strcmp() function. Also you really don't need the template<> for the C-string function.

You also don't need the "template" syntax on any of the function calls:

1
2
cout<<isLess<char*>(e, f)<<endl; // Don't need template syntax
cout << isLess(e, f) << endl;

Last edited on
@jlib : tnx for your response jlib, I understand that we could use overloaded function, but I wish to clarify this concept hence looking for template specialization.
Even if we add char * at line no. 33, it gives same error.

Here is the complete code :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include<iostream>
#include<cstring>

using namespace std;

template<typename T>
bool isLess(T &x, T &y)
{
	return x < y;
}

template<>
bool isLess(char *x, char *y)
{
    return (strcmp(x, y) < 0);
}


int main()
{
	int a(10), b(20);
	double c(2.0), d(3.0);
	cout<<isLess<int>(a,b)<<endl;
	cout<<isLess<double>(c,d)<<endl;
	//For above types, generic comparator works fine
	
	//but if we have to compare character represented string, how can we do this in our comparator
	//assume charaters strings are compared the same way as strcmp.
	
	
	char *e = "str1";
	char *f = "str2";
	cout<<isLess<>(e,f)<<endl;
	
	return 0;
}

Last edited on
You don't need the template syntax on your function calls. Something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <cstring>

using namespace std;

template<typename T>
bool isLess(T &x, T &y)
{
	return x < y;
}

// Note the lack of the template<> because it is not required.
bool isLess(char *x, char *y)
{
    return (strcmp(x, y) < 0);
}


int main()
{
	int a(10), b(20);
	double c(2.0), d(3.0);
	
	cout << isLess(a,b) << endl;
	cout << isLess(c,d) << endl;

	const char *e = "str1"; // Note the const here.
	const char *f = "str2";
	
	cout << isLess(e,f) << endl;

	return 0;
}


A little added whitespace wouldn't hurt either.

Also please note that, if we add char * at line no. 12, it works fine.

1
2
template<char *>
bool isLess(char *x, char *y)
Last edited on
> [Error] template-id 'isLess<>' for 'bool isLess(char*, char*)' does not match any template declaration

Overload resolution only selects a base template (or a nontemplate function, if one is available). Only after it's been decided which base template is going to be selected, and that choice is locked in, will the compiler look around to see if there happens to be a suitable specialization of that template available, and if so that specialization will get used.
http://www.gotw.ca/publications/mill17.htm


In the posted code, the base template expects references; the attempted specialisation (which expects values) is not looked at.

This would work:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <cstring>

template < typename T > bool isLess( const T& x, const T& y ) { return x < y ; }

// as a general principle, overload, don't specialise
// see: http://www.gotw.ca/publications/mill17.htm
bool isLess( const char* x, const char* y ) { return std::strcmp(x,y) < 0 ; }

int main()
{
    int a(10), b(20);
    double c(2.0), d(3.0);
    std::cout << isLess(a,b) << '\n' ;
    std::cout << isLess(c,d) << '\n' ;

    const char* e = "str12"; // const
    const char* f = "str1"; // const
    std::cout << isLess(e,f) << '\n' ;
}
@JLBorges : Tnx for having a look. I understand that if we use function overloading then we don't need to use template specialization.
But I am trying to understand how to use template specialization if we are not using funciton overloading.

In the below code, if instead of overloaded function we use template specialization as follows then we get error : [Error] template-id 'isLess<>' for 'bool isLess(const char*&, const char*&)' does not match any template declaration.

I thought that for specialization char * will be considered T and hence we can pass arguments as
"const T& e" (where T = char *) i.e. "const char *& e".

Could you kindly help me with why this is not working.

If we comment lines 9,10; code runs fine.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <cstring>

template < typename T > bool isLess( const T& x, const T& y ) { return x < y ; }

// as a general principle, overload, don't specialise
// see: http://www.gotw.ca/publications/mill17.htm

template<>
bool isLess( const char *&x, const char *&y ) { return std::strcmp(x,y) < 0 ; }

int main()
{
    int a(10), b(20);
    double c(2.0), d(3.0);
    std::cout << isLess(a,b) << '\n' ;
    std::cout << isLess(c,d) << '\n' ;

    const char* e = "str1"; // const
    const char* f = "str2"; // const
    //std::cout << isLess(e,f) << '\n' ;
}


const char * & (reference to non-const pointer to const char) does not specialize const T & (reference to const T)
We want to specialise
template < typename T > bool isLess( const T& x, const T& y ) { return x < y ; }
for T == const char*

So, T& == const char*& and const T& == const char* const&

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <cstring>

template < typename T > bool isLess( const T& x, const T& y ) { return x < y ; }

template<>
bool isLess( const char* const& x, const char* const& y ) { return std::strcmp(x,y) < 0 ; }

int main()
{
    int a(10), b(20);
    double c(2.0), d(3.0);
    const char* e = "str1"; // const
    const char* f = "str2"; // const

    std::cout << std::boolalpha << isLess(a,b) << '\n'
              << isLess(c,d) << '\n'
              << isLess(e,f) << '\n' ;
}

http://coliru.stacked-crooked.com/a/c2b64093f8526780

If const char* const& looks arcane, use a type alias and things become much clearer:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <cstring>

template < typename T > bool isLess( const T& x, const T& y ) { return x < y ; }

using cstr = const char* ; // 'cstr' is another name for the type 'const char*'

template<> // substitute cstr for T
bool isLess( const cstr& x, const cstr& y ) { return std::strcmp(x,y) < 0 ; }

int main()
{
    int a(10), b(20);
    double c(2.0), d(3.0);
    const char* e = "str1"; // const
    const char* f = "str2"; // const

    std::cout << std::boolalpha << isLess(a,b) << '\n'
              << isLess(c,d) << '\n'
              << isLess(e,f) << '\n' ;
}

http://coliru.stacked-crooked.com/a/0a475dc94e8c9794
Thanks @JLBorges for such nice detailed explanation :)
Topic archived. No new replies allowed.