Reputation: 117
I am currently reading a book about templates and it gives the following example:
#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()
{
char* a="apple";
char* p="peach";
::max(a,p); // OK, BUT
::max("apple","peach");// <<<< ERROR IN VISUAL STUDIO 2012, WHY?
}
I do not understand why VS2012 says:
error C2440: 'return' : cannot convert from 'const char *' to 'const char (&)[6]'
Can please someone explain this to me? The book says that this should be ok, but it's an old book, I am still waiting for the new one.
Upvotes: 1
Views: 571
Reputation: 24576
The reason for the error is the following:
A string literal is of type const char[X]
, with X being the length of the string, including the zero delimiter. In your case it's 6, so in total T
is const char[6]
.
Comparing C-arrays with relational operators is not possible, so the compiler applies the "array to pointer decay", meaning in (a < b)
, a and b are treated as pointers.
It seems as if Visual Studio applies that decay for the whole expression, i.e. it treats a and b as pointers in (b) : (a)
, too.
The whole expression then has the type char const*
, but the return type is T const&
, meaning const char(&)[6]
, and the pointer is not convertible backwards to that. I believe it to be a bug in VS2012.
Update:
Here's a standard quote to support the claim that this is a bug:
§5.16,4:
If the second and third operands are glvalues of the same value category and have the same type, the result is of that type and value category [...]
Sidenote 1: You include <string>
, wich is not possible. Be aware that string literals are not std::string
s, mostyl for backward compatibility reasons with C.
Sidenote 2: You assign string literals to char *
, wich is deprecated. String literals are of type char const[]
, so decaying them to char*
is a speciality, again due to backward compatibility reasons. If you indeed treat the string behind that pointer as non-const and write to them, you will get undefined behavior. So prefer char const*
, your compiler should warn you about those anyways.
Sidenote 3: In C++, pointers are not totally ordered. So the coparison of the decayed arrays, meaning of the pointers in the max function is implementation defined, wich you will want to avoid in portable programs. It has no meaning anyways, since it copares addresses, not the string literal contents.
Sidenote 4: You got lucky that "apple" and "peach" are of same length. If they were not, e.g. "apples" and "peaches", the compiler would see char literals of type const char[7]
and const char[8]
and would not know what T
should be: http://ideone.com/fp2WmJ
Upvotes: 1
Reputation: 6999
While instantiating the function template max
, the template parameter T
is deduced from the arguments. In your example, it is of type char const [6]
, an array of six characters.
The expression (a < b) ? (b) : (a)
will convert both (a)
and (b)
to a pointer (rule 5.16.6, "array-to-pointer (4.2), [...] standard conversions are performed on the second and third operands".)
The compiler will then try to convert the resulting char const *
to a T
(the return type), which is a char const[6]
, hence your error.
GCC does not exhibit this behavior because it doesn't convert the arrays to a pointer if they are of the same type, that is, an array of char of the same size. The error still appears as long as the two char arrays are of different size.
Anyway, comparing char arrays probably doesn't work the way you think, so it would be better to just use std::string
instead.
Upvotes: 0
Reputation: 21058
The issue is that in the second case, you T const&
is being deduced to const char(&)[6]
, which is a reference to an array of 6 characters.
In VS2012, when you do the comparison using the ?:
, it's decaying the array references to char*
, which is then not able to be returned as a reference. GCC 4.7.2 doesn't exhibit this decay (See https://ideone.com/yIBZWi ).
Can you try the less compact version below (I don't have VS2012 on hand)? It should be able to avoid the decay by not using a and b together in the return.
template <typename T>
inline T const& max (T const& a, T const& b)
{
if(a<b)
return b;
else
return a;
}
Upvotes: 3