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

Sep 27, 2016 at 9:25pm
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;
}
Sep 28, 2016 at 12:14am
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.
Sep 28, 2016 at 4:36pm
@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 Sep 28, 2016 at 4:43pm
Sep 28, 2016 at 4:53pm
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 Sep 28, 2016 at 4:57pm
Sep 28, 2016 at 4:59pm
@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 Sep 28, 2016 at 5:07pm
Sep 28, 2016 at 5:09pm
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.

Sep 28, 2016 at 5:12pm
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 Sep 28, 2016 at 5:12pm
Sep 28, 2016 at 5:12pm
> [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' ;
}
Sep 28, 2016 at 5:39pm
@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' ;
}


Sep 28, 2016 at 5:56pm
const char * & (reference to non-const pointer to const char) does not specialize const T & (reference to const T)
Sep 28, 2016 at 5:59pm
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
Sep 28, 2016 at 7:02pm
Thanks @JLBorges for such nice detailed explanation :)
Topic archived. No new replies allowed.