views:

68

answers:

5

I wrote a template function to compare two variables:

template <class t>

int compare(const t &a, const t &b) {

if(a>b) return 1;

if (a<b) return -1;

return 0;

}

int main(int argc, const char *argv[])
{

    cout << compare("hi","world");

    return 0;

}

I get the following error

../src/templates.cpp: In function ‘int main(int, const char**)’:
../src/templates.cpp:11: error: no matching function for call to ‘compare(const char [3], const char [6])

Please explain. Also if I write cout << compare("hi", "wo"); it compiles properly. Or if I remove the & and declare the function like int compare(const t a, const t b) it compiles.

Please explain this behaviour to me.

+1  A: 

In C++, as in C, string literals are nul-terminated character arrays. "hi" becomes the character array ['h', 'i', 0]. C++ treats the size of the array as part of its type for templates; the string "hi" is an array of length 3, and the string "world" is an array of length 6, so the compiler can't find a single type t that matches both arrays.

When you try to compile compare("hi", "wo") the compiler finds that type t is const char [3], since both strings -- arrays -- have the same length.

When you drop the &, the arrays decode into const pointers, so the compiler finds that type t is const char *. Note that in this case, you're comparing the pointers to the strings, not their contents.

Commodore Jaeger
+2  A: 

A string literal of N characters is an array of N constant characters with a terminating '\0' afterwards. So type of "hi" is char const[3] and the one of "world" is char const[6].

So if you pass it to the template, t is deduced to two different types. Note that when in a reference parameter, template argument deduction does not transform arrays to pointers.

Also, please check up in comparing pointers to each other. The way you do that won't ever compare the strings lexically, but just the addresses of them, yielding an unspecified value. You can fix the argument deduction bit by having two separate template parameters

template <class t, class u>
int compare(const t &a, const u &b) {
  if(a>b) return 1;
  if (a<b) return -1;

  return 0;
}

Clang gives a good error message

main1.cpp:17:5: error: no matching function for call to 'compare'
    compare("hi","world");
    ^~~~~~~
main1.cpp:4:5: note: candidate template ignored: 
  deduced conflicting types for parameter 't' ('char [3]' vs. 'char const[6]')
int compare(const t &a, const t &b) {
    ^
1 error generated.
Johannes Schaub - litb
A: 

The type of the string literal "hi" is const char [3]. The type of the string literal "world" is const char [6]. So they're different types.

Your compare template uses the same type for both parameters, so this won't fly.

Change your compare template to use different types for the two parameters. Also you will have to provide an overload to compare string literals correctly.

If you just write (a>b), the arrays a and b will decay into pointers, so you'd be really only comparing the addresses of the first characters of the string literals.

pgroke
A: 

Template functions are converted into real function with template classes replaced with real classes by the compiler. In your code, your function prototype indicates that,

int compare(const t &a, const t &b)

both a and b must be of same type.

When you call compare("hi", "world");, for compiler there are two different types for a and b because "hi" is of type const char [3] and "world" is of type const char [6]. The compiler cannot implement a good version of compare().

But when you call compare("hi", "wo");, suddenly both becomes of same type: const char [3] and there is no ambiguity.

If you implement the function as int compare(const t a, const t b) the compiler finds an alternative for t: char *. The arrays will be converted into const char * and thus there is no ambiguity.

Donotalo
A: 

If you need to have the comparison operators work in a template for C-style strings (null terminated arrays of char), you will have to have a specialized template along with your generic template. The thorn is that C-style strings cannot be compared using the operators.

template <class t>
int compare(const char * a, const char * b)
{
  return strcmp(a, b);
}

template <class t>
int compare(const t& a, const t& b)
{
  return (a == b) ? 0 : ((a < b) ? -1 : 1);
}

Note: This templated function will fail for all classes that do not have operator< or operator== defined. Which will cause havoc down the road. This looks very much like a using C++ templates to force things into the C language methodology.

A Better Approach

I see two C++ specific features that could make your life easier: Overloading operators and using std::string. Using these features will eliminate your need for the templated functions.

Overloading Operators

One reason for having a templated compare function is because the class doesn't have comparison operators devined (otherwise you would use the operators). So, define the overloaded operators < and ==. The other operators >, >=, <= and != can be defined in terms of the latter two. See boost operators header.

Using std::string

The problem of using comparison operators on C-style strings can be removed by replacing them with std::string. A nice feature of std::string is that it has the comparison operators already defined. Thus you can use < to compare two std::string objects.

Summary

Rather than creating a template function that uses C-style comparing (e.g. return -1, 0, or +1), define comparison operators for your classes and convert null terminated arrays of characters into instances of std::string.

Thomas Matthews