简体   繁体   中英

Dangling pointers/references to int and char* constants

I'm reading a book on C++ templates by Vandevoorde, Josuttis and Gregor and do not understand the warning they make about dangling reference. Here is the code:

#include <cstring>

// maximum of two values of any type (call-by-reference)
template<typename T>
T const& max (T const& a, T const& b)
{
  return  b < a ? a : b;
}

// maximum of two C-strings (call-by-value)
char const* max (char const* a, char const* b)
{
  return  std::strcmp(b,a) < 0  ? a : b;
}

// maximum of three values of any type (call-by-reference)
template<typename T>
T const& max (T const& a, T const& b, T const& c)
{
  return max (max(a,b), c);       // error if max(a,b) uses call-by-value
}

int main ()
{
  auto m1 = ::max(7, 42, 68);     // OK

  char const* s1 = "frederic";
  char const* s2 = "anica";
  char const* s3 = "lucas";
  auto m2 = ::max(s1, s2, s3);    // run-time ERROR
}

The given message is that for C-string the nested max(a,b) creates a dangling reference, whereas for int's it does not. So what is so special about pointer to char in comparison to reference to int, given that both are implemented as pointers to objects allocated outside max functions?

This:

   char const* max (char const* a, char const* b)

returns a nameless temporary pointer value, and then this:

    return max (max(a,b), c);

returns a reference to it.

... do not understand the warning they make about dangling reference

::max(s1, s2, s3) uses template<typename T> T const& max (T const& a, T const& b, T const& c) returning a reference

If the definition of template<typename T> T const& max (T const& a, T const& b, T const& c) is changed to be :

template<typename T>
T const& max (T const& a, T const& b, T const& c)
{
  return (a > b) ? ((a > c) ? a : c)
                 : ((b > c) ? b : c);
}

there is no problem because it already have the references.

But with ::max(s1, s2, s3) T is const char* so in max (max(a,b), c) max is char const* max (char const* a, char const* b) whose does not return a reference, because of that the compiler saves the result of char const* max (char const* a, char const* b) in a temporary variable on the stack and return a reference to that temporary variable, producing your message and the associated problem. It is like if you do int & f() { int v = 0; return v; } int & f() { int v = 0; return v; } int & f() { int v = 0; return v; } except that the temporary variable is created by the compiler itself.

Of course the problem disappears with template<typename T> T const max (T const& a, T const& b, T const& c) ( returning a value rather than a reference) because the value returned by char const* max (char const* a, char const* b) can be directly returned.

Note for ::max(7, 42, 68) there is no problem because max in max (max(a,b), c) is template<typename T> T const& max (T const& a, T const& b) which returns a reference.

To continue to return a reference in the other case you can specialize max for char * , for instance :

// maximum of two C-strings (call-by-value)
template<>
char const* const & max (char const* const & a, char const* const & b)
{
  return  std::strcmp(b,a) < 0  ? a : b;
}

or define it as

char const* const & max (char const* const & a, char const* const & b)
{
  return  std::strcmp(b,a) < 0  ? a : b;
}

whose returns the reference the version with the three parameters can use without having to use a temporary variable and return a reference to it.

(Personally I prefer the specialization because it seems natural having a template version)


#include <cstring>
#include <iostream>

// maximum of two values of any type (call-by-reference)
template<typename T>
T const& max (T const& a, T const& b)
{
  return  b < a ? a : b;
}

// MODIFIED
// maximum of two C-strings (call-by-value)
template<>
char const* const & max (char const* const & a, char const* const & b)
{
  return  std::strcmp(b,a) < 0  ? a : b;
}

// maximum of three values of any type (call-by-reference)
template<typename T>
T const& max (T const& a, T const& b, T const& c)
{
  return max (max(a,b), c);       // error if max(a,b) uses call-by-value
}

int main ()
{
  auto m1 = ::max(7, 42, 68);     // OK

  char const* s1 = "frederic";
  char const* s2 = "anica";
  char const* s3 = "lucas";
  auto m2 = ::max(s1, s2, s3);    // run-time ERROR

  std::cout << m2 << std::endl; // << ADDED TO CHECK
}

Compilation and execution :

pi@raspberrypi:/tmp $ g++ -pedantic -Wall -Wextra s.cc
s.cc: In function ‘int main()’:
s.cc:28:8: warning: unused variable ‘m1’ [-Wunused-variable]
   auto m1 = ::max(7, 42, 68);     // OK
        ^~
pi@raspberrypi:/tmp $ ./a.out
lucas

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM