In which cases does compiler create a new temporary local value while comparing two types?
template <typename T> inline T const& max (T const& a, T const& b)
{
return a < b ? b : a;
}
inline char const* max (char const* a, char const* b)
{
return std::strcmp(a,b开发者_开发百科) < 0 ? b : a;
}
template <typename T> inline T const& max (T const& a, T const& b, T const& c)
{
return max (max(a,b), c);
}
int main ()
{
const char* s1 = "frederic";
const char* s2 = "anica";
const char* s3 = "lucas";
::max(s1, s2, s3);
}
For the above code, the book (C++ Templates - Wesley) says:
The problem is that if you call max() for three C-strings, the statement return max (max(a,b), c); becomes an error. This is because for C-strings, max(a,b) creates a new, temporary local value that may be returned by the function by reference.
Now, my question is that in which cases compiler automatically creates and returns temporary variables, and why (including this one)?
Oh! Does it mean that compiler creates a temporary variable for storing each value and then returns those compared temporary variables? Are there other cases too, in which this is done?
Let's do it slowly:
::max(s1, s2, s3);
invokes T const& max (T const& a, T const& b, T const& c)
with T
being char const*
.
Within this method we have:
max(a,b)
which invokes char const* max (char const* a, char const* b)
max(max(a,b), c);
thus invokes char const* max (char const* a, char const* b)
as well
And thus the dreaded part (if we rewrite):
template <typename T>
T const& max (T const& a, T const& b, T const& c)
{
T result = max(max(a,b), c);
return result; // reference to a temporary
}
Because T
is char const*
and max(char const*, char const*)
returns a copy (of the pointer) and not a const reference to the pointer.
gcc
helpfully diagnose the issue (see ideone):
prog.cpp: In function ‘const T& max(const T&, const T&, const T&) [with T = const char*]’:
prog.cpp:27: instantiated from here
prog.cpp:18: warning: returning reference to temporary
With 18
being the return max(....)
in the template and 27
being the invocation.
If you rewrite max
to "hide" this temporary:
template <typename T> inline T const& max (T const& a, T const& b, T const& c)
{
T const& result = max (max(a,b), c);
return result;
}
You will foil gcc's detection (see ideone), but this does not solve the issue. Clang would helpfully warn that T const& result
is bound to a temporary.
The real problem is that char const* max(char const*, char const*)
is returning a value so either all max
should return by values or all max
should take and return const references... if you wish to mix them up.
It's not really anything to do with the compiler; this isn't one of those questions of how compilers make and optimise temporaries when it's discretionary for the implementation. What's going on is just a simple question of variable scope.
The second function down is returning by value, that is, there is a slot in memory which is destined to hold the return value of the function, and the value of a or b is copied into it. So, it returns a number which is a memory address, either the same address as is in a, or in b. Out of those two strings, one of the addresses is stored twice in memory in two different places.
So, the outer max in the third function: it's going to return by reference either c (which is safe), or the temporary (which is unsafe). The temporary is located just above the stack top in this case, and returning a reference to it is like returning a pointer, in this case to unsafe memory.
Either you could clean the whole thing up to use C-strings if that's the idea, or else fix up function 2 to return safe references.
The big problem here is actually the fact, that the const char*
variant is unrelated to the template. This is a design problem. If you have a template, you shouldn't create unrelated overloads.
Now if you would create a specialization:
template <> const char* max<const char*> (const char* a, const char* b);
The compiler will tell you that is isn't compatible with the original template.
The correct version is actually:
template <> const char * const & max<const char*> (const char* const &a, const char* const &b);
The problem is that the overload char const* max (char const* a, char const* b)
returns value, not reference. So temporary variable is created inside template <typename T> inline T const& max (T const& a, T const& b, T const& c)
and returned by reference, what is bad.
The problem is inconsistency between templated version and the overload. The overload should looks like:
inline char const* const& max (char const* const& a, char const* const& b)
精彩评论