Solved

Could someone try to explain this weird compiler behaviour

Posted on 2003-11-18
8
801 Views
Last Modified: 2013-12-14
Hi,

In reference to this thread ' http://www.experts-exchange.com/Programming/Programming_Languages/Cplusplus/Q_20800597.html ' I had some difficulties with one of the classes I'm working on. Well I decided to take a look at how Microsoft is doing these things and what do I find out? They have the same code as I have, but theirs is working?! The same you say, yes the same. Are you sure, hey look, there IS something different. Find the two differences between the code :-) (I posted the solutation below it ;-))

class CTest
{
public:
      CTest() throw()
      {
            m_pszData = new TCHAR[1024];
      }

      ~CTest()
      {
            delete [] m_pszData;
      }

      TCHAR& operator[] (int iChar) const throw()
      {
            return m_pszData[iChar] ;
      }

      operator LPCTSTR() const throw()
      {
            return m_pszData ;
      }

private:
      LPTSTR m_pszData;
};

=============================================

class CTest
{
public:
      CTest() throw()
      {
            m_pszData = new TCHAR[1024];
      }

      ~CTest()
      {
            delete [] m_pszData;
      }

      TCHAR& operator[] (UINT iChar) const throw()
      {
            return m_pszData[iChar] ;
      }

      operator LPCTSTR() const throw()
      {
            return m_pszData ;
      }

private:
      LPTSTR m_pszData;
};

The first one compiles, the second one doesn't. The second one gives an error that the operator are ambigious. Why? Because if you use the [] operator on the second one, the compiler has two choices:
1) Use the provided [] operator
2) Use the operator to cast to LPCTSTR and then use the default [] operator for LPCTSTR

So why isn't it doing that at the first example? Well because of a very small change in the [] operator. They use an int instead of an unsigned int.

Now I hope someone is able to explain me why changing the signed/unsigned state of a parameter can make such a big difference.
0
Comment
Question by:LuCkY
  • 4
  • 2
  • 2
8 Comments
 
LVL 15

Accepted Solution

by:
efn earned 63 total points
Comment Utility
Welcome to the dark and mysterious world of function overload resolution.

When there is more than one candidate for a function call, the compiler tries to pick the one that requires the fewest and least drastic conversions.  Converting the object reference to LPCTSTR is a conversion, and converting a signed int subscript to unsigned is also a conversion.  (Signed int is the type of a plain old numeric literal without a decimal point or leading zero.) There is a ranking of conversions--some are considered less drastic and therefore preferred to others, but the compiler apparently considers converting the subscript to unsigned or converting the object reference to a pointer equally disgusting, so you get the ambiguity error.  When the operator [] parameter is int, no conversion is necessary for a call to operator [], so that function beats out operator LPCTSTR.

That means that if you use an unsigned subscript, the second one will compile and the first one won't.

This doesn't quite match up with what I've read about overload resolution, which says that the compiler should prefer a standard conversion such as signed to unsigned to a user-defined conversion, which I think is what operator LPCTSTR is.  I reproduced your result with Microsoft Visual C++ 6.0, but Borland C++Builder 4 was quite happy to compile either version with a signed int subscript, so maybe it follows the standard better in this detail.

--efn
0
 
LVL 2

Assisted Solution

by:ext2
ext2 earned 62 total points
Comment Utility
Very interesting.  MSVC++6 gives little info, but running this on the free/open GNU g++ compiler (available in Cygwin, www.cygwin.com, under Windows) reports the following on the second example:

op2.cc: In function `int main()':
op2.cc:41: error: ISO C++ says that `TCHAR& CTest::operator[](unsigned int)
   const' and `operator[]' are ambiguous even though the worst conversion for
   the former is better than the worst conversion for the latter

Moreover, if we put both

  TCHAR& operator[] (UINT iChar) const throw()

and

  TCHAR& operator[] (int iChar) const throw()

into the class, the error doesn't occur!  and running something like

  cout << test[(UINT)2] << test[2];

invokes each method in turn.

The problem has something to do with the

  operator LPCTSTR() const throw()

causing an implicit conversion of the object into a C string, which in turn has an "operator[]" operator built-in as well.  That's where the ambiguity is, but I'm not sure what the specific rules are how it choosing one of multiple functions.  Why keep it simple and replace

  operator LPCTSTR() const throw()

with

  LPCTSTR c_str() const throw()

as the STL string class does?
0
 
LVL 2

Expert Comment

by:ext2
Comment Utility
What efn said about

  "That means that if you use an unsigned subscript, the second one will compile and the first one won't."

I've also verified as true on MSVC++6, but on g++ both compile if you use an unsigned subscript, and in both cases the "operator[] (int/UNIT iChar)" gets invoked rather than the "operator LPCTSTR()".  As I mentioned before, MSVC++6 and g++ behave identically, however, if a signed subscript is used.

0
 
LVL 2

Expert Comment

by:ext2
Comment Utility
What efn said about

  "That means that if you use an unsigned subscript, the second one will compile and the first one won't."

I've also verified as true on MSVC++6, but on g++ both compile if you use an unsigned subscript, and in both cases the "operator[] (int/UNIT iChar)" gets invoked rather than the "operator LPCTSTR()".  As I mentioned before, MSVC++6 and g++ behave identically, however, if a signed subscript is used.

0
Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 1

Author Comment

by:LuCkY
Comment Utility
Very interesting that the compiler thinks that using an signed integer w/o the conversion will be faster, which I seriously doubt. Assuming that we always take the path of the user defined [] operator there are two possibilities:

TCHAR& operator [] (UINT uPos) const throw()
{
    // Only error checking for upper bounds has to be done, so one if statement
}

TCHAR& operator [] (int nPos) const throw()
{
    // Here however I also have to check the lower bounds
}

I wonder if that a signed to unsigned conversion, especially if you use a constant value such as in our tests (ie strString[0] = 'T') is faster than the extra if statement required if the conversion is not needed.

>> Why keep it simple and replace

So I can pass the string to functions that require a LPCTSTR without calling a member function to increase readability. I think the STL string also has a LPCTSTR operator hasn't it? Well I guess the follow up will be in compiler madness part #2 :-)
0
 
LVL 15

Expert Comment

by:efn
Comment Utility
> Very interesting that the compiler thinks that using an signed integer w/o the conversion will be faster, which I seriously doubt.

The compiler is not making any judgements about how fast operations might be.  The language designers had to make some rules for resolving function overloads, they wrote the rules to favor the best match, and they decided that signed int to signed int was a better match than signed int to unsigned int.

> I think the STL string also has a LPCTSTR operator hasn't it?

No, they intentionally left it out, because they thought it was dangerous.  Not to mention that "LPCTSTR" is a Microsoftism that probably wouldn't be used in a standard.

--efn
0
 
LVL 1

Author Comment

by:LuCkY
Comment Utility
>> Not to mention that "LPCTSTR" is a Microsoftism that probably wouldn't be used in a standard.

Well const TCHAR * then :-)

>> The compiler is not making any judgements about how fast operations might be

It is, the compiler changes a lot of your code -if it is safe to do so- if it can speed up things (depending on the optimization settings too of course). In my example it wont spot the extra if statement of course. That was just to give an example of why something that the compiler considers to be faster can actually be slower.

>> and they decided that signed int to signed int was a better match than signed int to unsigned int.

That's not exactly what's happening. They decided a user defined conversion (from which the speed is unknown, might as well have a huge loop in it) and after that using an operator with a signed int is faster than converting a signed int to unsigned int. Makes sense doesn't it :-D
0
 
LVL 2

Expert Comment

by:ext2
Comment Utility
As efn implies, the use of "faster" is misleading as it suggests how long it takes for the compiled code to execute at run-time (i.e. performance).  "simpler" or "fewer steps" is better as it suggests how many logical steps are in the transformation that is to be done at compile-time regardless how fast it takes the compiler to perform those transformations or how fast the compiled code actually runs.

0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

Suggested Solutions

  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
Introduction This article is a continuation of the C/C++ Visual Studio Express debugger series. Part 1 provided a quick start guide in using the debugger. Part 2 focused on additional topics in breakpoints. As your assignments become a little more …
The viewer will learn how to use and create keystrokes in Netbeans IDE 8.0 for Windows.
The viewer will learn how to synchronize PHP projects with a remote server in NetBeans IDE 8.0 for Windows.

763 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

7 Experts available now in Live!

Get 1:1 Help Now