Reputation: 951
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?
Upvotes: 1
Views: 280
Reputation: 32586
... 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; }
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
Upvotes: 1
Reputation:
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.
Upvotes: 3