1

I'm subclassing the MFC class CString (nothing wrong with the class, but trust me, I need to do this for specific implementation). I've succesfully customized some behaviors, but I noticed that I lost the implicit (LPCTSTR) operator that seems to occur when a CString is passed into a format string. This magic happens whether it's CString::Format or prinf/sprintf. For example:

CString Str = _T("Really cool string");
TCHAR szBuffer[32];
_stprintf(szBuffer, _T("Here it is: %s"), Str);

I haven't figured out how this magic works with a standard CString, since CString::FormatString just passes the variable argument list through to _vswprintf and _swprintf. But whatever it does is missing in my derived class.

The operator (LPCTSTR) is inherited as expected, and works when explicitly called.

Any ideas?

2 Answers2

3

Your assumption is wrong: There is no implicit conversion to LPCTSTR when a CString object is passed to a printf-style function. The compiler has no way of knowing that that's what you want - it's not going to parse the format string to deduce type information.

Instead, your CString object is passed to printf as is. The magic here is that the CString authors predicted wrong assumptions about when and how the implicit cast operator is invoked and modeled CString to be compatible with C-strings. To do so, CString contains a single LPTSTR pointer and no v-table. Now if a CString object is passed to a printf-style function, only this pointer will wind up as a parameter, and everything appears to work. Note that appearing to work is a valid form of undefined behavior. This is undefined behavior.

If you're wondering where CString stores the remaining information (current size, capacity, etc.), it resides in memory just ahead of the character buffer. This way all the information is available through a single pointer:

CStringData* GetData() const throw() {
    return( reinterpret_cast< CStringData* >( m_pszData )-1 );
}

Now to solve your real problem: Don't rely on undefined behavior, use explicit casts where necessary and nobody will get hurt:

_stprintf(szBuffer, _T("Here it is: %s"), static_cast<LPCTSTR>(Str));

As an alternative to the cast you can call CString::GetString():

_stprintf(szBuffer, _T("Here it is: %s"), Str.GetString());

Also keep in mind: Deriving from a class that does not provide a virtual destructor is a resource leak waiting to happen. In other words: Do not derive from CString.

IInspectable
  • 46,945
  • 8
  • 85
  • 181
0

When you derive from a class, not all operators are inherited. Google the topic of operator inheritance in C++. In your derived class, you may need to implement the operators and simply forward to the base class.

tolanj
  • 3,651
  • 16
  • 30