Considering this code fragment:
struct My {
operator const char*()const{ return "my"; }
} my;
CStringA s( "aha" );
printf("%s %s", s, my );
// another variadic function to get rid of comments about printf :)
void foo( int i, ... ) {
va_list vars;
va_start(vars, i);
for( const char* p = va_arg(vars,const char*)
; p != NULL
; p=va_arg(vars,const char*) )
{
std::cout << p << std::endl;
}
va_end(vars);
}
foo( 1, s, my );
This snippet results in the 'intuitive' output "aha". But I haven't got a clue how this can work:
CStringA*
that is interpreted as a const char*
operator (const char*)
on it, why wouldn't it do so for my own class?Can someone explain this?
EDIT: added a dummy variadic function that treats it's arguments as const char*
s. Behold - it even crashes when it reaches the my
argument...
The relevant text of C++98 standard §5.2.2/7:
The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conversions are performed on the argument expression. After these conversions, if the argument does not have arithmetic, enumeration, pointer, pointer to member, or class type, the program is ill-formed. If the argument has a non-POD class type (clause 9), the behavior is undefined.
So formally the behavior is undefined.
However, a given compiler can provide any number of language extensions, and Visual C++ does. The MSDN Library documents the behavior of Visual C++ as follows, with respect to passing arguments to ...
:
- If the actual argument is of type float, it is promoted to type double prior to the function call.
- Any signed or unsigned char, short, enumerated type, or bit field is converted to either a signed or an unsigned int using integral promotion.
- Any argument of class type is passed by value as a data structure; the copy is created by binary copying instead of by invoking the class's copy constructor (if one exists).
This doesn’t mention anything about Visual C++ applying user defined conversions.
MS CString
is "cleverly" layed out, so that it's POD representation is exactly the pointer to its null terminated character string. (sizeof(CStringA) == sizeof(char*)
) When it is used in any printf-style function the function just get's passed the character pointer.
So this works because of the last point above and the way CString
is layed out.
What you're doing is undefined behaviour, and is either a non-standard extension provided by your compiler or works by sheer luck. I'm guessing that the CString stores the string data as the first element in the structure, and thus that reading from the CString
as if it were a char *
yields a valid null-terminated string.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With