5.6 Using String Literals as Arguments for Function Templates

Ru-Brd

Passing string literal arguments for reference parameters of function templates sometimes fails in a surprising way. Consider the following example:

  // basics/max5.cpp  #include <string>  // note: reference parameters  template <typename T>  inline T const& max (T const& a, T const& b)  {      return a < b ? b : a;  }  int main()  {      std::string s;      ::max("apple","peach");  // OK: same type  ::max("apple","tomato");  // ERROR: different types  ::max("apple",s);  // ERROR: different types  } 

The problem is that string literals have different array types depending on their lengths. That is, "apple" and "peach" have type char const[6] whereas "tomato" has type char const[7] . Only the first call is possible because the template expects both parameters to have the same type. However, if you declare nonreference parameters, you can substitute them with string literals of different size:

  // basics/max6.cpp  #include <string>  // note: nonreference parameters  template <typename T>  inline T max (T a, T b)  {      return a < b ? b : a;  }  int main()  {      std::string s;      ::max("apple","peach");  // OK: same type  ::max("apple","tomato");  // OK: decays to same type  ::max("apple",s);  // ERROR: different types  } 

The explanation for this behavior is that during argument deduction array-to-pointer conversion (often called decay ) occurs only if the parameter does not have a reference type. This is demonstrated by the following program:

  // basics/refnonref.cpp  #include <typeinfo>  #include <iostream>  template <typename T>  void ref (T const& x)  {      std::cout << "x in ref(T const&): "                << typeid(x).name() << '\n';  }  template <typename T>  void nonref (T x)  {      std::cout << "x in nonref(T): "                << typeid(x).name() << '\n';  }  int main()  {      ref("hello");      nonref("hello");  } 

The example passes a string literal to function templates that declare their parameter to be a reference or nonreference respectively. Both function templates use the typeid operator to print the type of the instantiated parameters. The typeid operator returns an lvalue of type std::type_info , which encapsulates a representation of the type of the expression passed to the typeid operator. The member function name() of std::type_info is intended to return a human-readable text representation of the latter type. The C++ standard doesn't actually say that name() must return something meaningful, but on good C++ implementations , you should get a string that gives a good description of the type of the expression passed to typeid (with some implementations this string is mangled , but a demangler is available to turn it into human-readable text). For example, the output might be as follows :

 x in ref(T const&): char [6]  x in nonref(T):     const char * 

If you encounter a problem involving a mismatch between an array of characters and a pointer to characters , you might have stumbled on this somewhat surprising phenomenon . [3] There is unfortunately no general solutions to address this problem. Depending on the context, you can

[3] In fact, this is the reason that you cannot create a pair of values initialized with string literals using the original C++ standard library (see [Standard98]):

 std::make_pair("key","value")  // ERROR according to [Standard98]  

This was fixed with the first technical corrigendum by replacing the reference parameters of make_pair() by nonreference parameters (see [Standard02]).

  • use nonreferences instead of references (however, this can lead to unnecessary copying)

  • overload using both reference and nonreference parameters (however, this might lead to ambiguities ; see Section B.2.2 on page 492)

  • overload with concrete types (such as std::string )

  • overload with array types, for example:

     template <typename T, int N, int M>  T const* max (T const (&a)[N], T const (&b)[M])  {      return a < b ? b : a;  } 
  • force application programmers to use explicit conversions

In this example it is best to overload max() for strings (see Section 2.4 on page 16). This is necessary anyway because without overloading in cases where the call to max() is valid for string literals, the operation that is performed is a pointer comparison: a<b compares the addresses of the two string literals and has nothing to do with lexicographical order. This is another reason why it is usually preferable to use a string class such as std::string instead of C-style strings.

See Section 11.1 on page 168 for details.

Ru-Brd


C++ Templates
C++ Templates: The Complete Guide
ISBN: 0201734842
EAN: 2147483647
Year: 2002
Pages: 185

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net