Sign in to follow this  
Mantear

C++ MSVS 2005 Express compiler bug?

Recommended Posts

Greetings! I've got a real head-scratcher here as to why the compiler (MSVS 2005 Express) is compiling something with no errors. Here's the class:
class TestClass
{
public:
    TestClass(){m_Width = 0;}
    ~TestClass(){}
    unsigned int Width(){ return m_Width; }
    void Width(unsigned int width){ m_Width = width; }

private:
    unsigned int m_Width;
};
I made the following mistake in my code:
TestClass object;
if (object.Width > 0)
{ ... }
else
{ ... }
This code snippet compiled with no errors or warnings! It was failing the check that it was supposed to pass and processing the else statement, causing me to take a closer look at that section when I noticed the missing '()'. Even run-time there was no indication of anything going wrong other than the check failing when it shouldn't have. If I remove either declarations of Width(), it won't compile with the normal expected errors. What's going on here?

Share this post


Link to post
Share on other sites
It's correct behvaior, but obviously not what you wanted. What it's actually doing is comparing the function istelf (a pointer to that function) to zero. Depending on the address of the code (which probably is an address like 0x8...), the value (when interpreted as a signed value) is negative, so the else clause is taken.

It's an obnoxious bug that comes up every so often. In many cases, the necessary casting to trigger this problem causes the compiler to emit a warning or error. But in some cases (like this one), the surrounding code is simple enough that it just happens silently.

Share this post


Link to post
Share on other sites
Quote:
It's correct behvaior, but obviously not what you wanted. What it's actually doing is comparing the function istelf (a pointer to that function) to zero.

A valiant attempt, but this is not the case here. Forming a pointer-to-member would be required to compare the function's address to zero, and that requires explicit use of the address-of operator and a slightly different syntax in general.

To the OP, there is more to your problem then you have extracted and shown here.
The following code, identical to yours but for the creation of a main() function to hold the offending code, produces error C3867 (complaining that Width() is missing a function call argument list) as expected:

#include <iostream>

class TestClass
{
public:
TestClass(){m_Width = 0;}
~TestClass(){}
unsigned int Width(){ return m_Width; }
void Width(unsigned int width){ m_Width = width; }

private:
unsigned int m_Width;
};

int main()
{
TestClass object;
if (object.Width > 0)
{
std::cout << "TRUE";
}
else
{
std::cout << "FALSE";
}
}


Perhaps you have a macro in scope that is doing something funky? Try to pare down the example further.

Share this post


Link to post
Share on other sites
1>.\Main.cpp(16) : error C3867: 'TestClass::Width': function call missing argument list; use '&TestClass::Width' to create a pointer to member

That's what my VS2k5 SP1 tells me.
Some compilers sometimes lets you get away with omitting the & to get a function pointer, which might be what you're experiencing.

Share this post


Link to post
Share on other sites
Entire source code of a test project using VisualC++ 2005 Express Edition (Version 8.0.50727.42)

Header: TestClass.h
class TestClass
{
public:
TestClass(){m_Width = 10;}
~TestClass(){}
unsigned int Width(){ return m_Width; }
void Width(unsigned int width){ m_Width = width; }

private:
unsigned int m_Width;
};


Source: TestClass.cpp
// TestClass.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "TestClass.h"

int _tmain(int argc, _TCHAR* argv[])
{
TestClass object;
bool success;

if (object.Width > 0)
{
success = true;
}
else
{
success = false;
}

return 0;
}


Build results:
------ Build started: Project: TestClass, Configuration: Debug Win32 ------
Compiling...
stdafx.cpp
Compiling...
TestClass.cpp
Compiling manifest to resources...
Linking...
Embedding manifest...
Build log was saved at "file://c:\3dEngine\Test\TestClass\TestClass\Debug\BuildLog.htm"
TestClass - 0 error(s), 0 warning(s)
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========


Running the program takes it down the
success = false
path.
???

Share this post


Link to post
Share on other sites
The fact that Zao and I are using SP1 might be the difference -- perhaps an appropriate extension to allow pointer-to-member to be formed without the address-of was removed in SP1. I'm somewhat skeptical of that, however, but I haven't dug up a changelist to see.

You could try going to the project options and disabling language extensions to see if it fails then. Also, what's in stdafx.h or anything included by stdafx.h?

Share this post


Link to post
Share on other sites
I tried it with Language Extensions disabled (/Za) but nothing changed.

stdafx.h is the default file generated by VS. It's contents are:
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//

#pragma once


#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
#include <stdio.h>
#include <tchar.h>



// TODO: reference additional headers your program requires here


How do I go about getting SP1? What are the main improvements with SP1?

Share this post


Link to post
Share on other sites
It compiles for me :(
No SP1, fresh VS install.
Disabled precompiled header.

Even after I examined generated asembly, I cannot find what it's doing:
//... object has just been constructed (eax=this)
0040103E xor eax,eax
00401040 je main+18h (401048h) // else

Share this post


Link to post
Share on other sites
Quote:
Even after I examined generated asembly, I cannot find what it's doing:
//... object has just been constructed (eax=this)
0040103E xor eax,eax
00401040 je main+18h (401048h) // else

You could try after turning off optimization. Here the compiler recognizes that a function pointer will never be 0 (or NULL), and hence always performs the else clause.

Share this post


Link to post
Share on other sites
Quote:
Original post by jpetrieperhaps an appropriate extension to allow pointer-to-member to be formed without the address-of was removed in SP1.


Pointers to members were allowed to be declared without the & operator before SP1. SP1 did remove this extension, and now they actually issue an error if you leave it out. For example:

error C3867: 'Foo::FooCall': function call missing argument list; use '&Foo::FooCall' to create a pointer to member

Share this post


Link to post
Share on other sites
Quote:
Original post by Dranith
Quote:
Original post by jpetrieperhaps an appropriate extension to allow pointer-to-member to be formed without the address-of was removed in SP1.


Pointers to members were allowed to be declared without the & operator before SP1. SP1 did remove this extension, and now they actually issue an error if you leave it out. For example:

error C3867: 'Foo::FooCall': function call missing argument list; use '&Foo::FooCall' to create a pointer to member


It's strange that for my example to work, I simply need to make an overload function. Once a function becomes overloaded, the pointer to member conversion happens automatically when doing something like 'object.Width'.

I'll have to look into upgrading to SP1. Are there any down-sides to doing so? What are the major improvements?

Share this post


Link to post
Share on other sites
Quote:
Original post by jpetrie
The fact that Zao and I are using SP1 might be the difference -- perhaps an appropriate extension to allow pointer-to-member to be formed without the address-of was removed in SP1. I'm somewhat skeptical of that, however, but I haven't dug up a changelist to see.


The results of your program, which compile fine on my machine (which lacks SP1, using 0 modifications to the source listed):

FALSEPress any key to continue . . . _

Share this post


Link to post
Share on other sites
After upgrading to SP1, I now get this error when comiling the above code:
testclass.cpp(12) : error C3867: 'TestClass::Width': function call missing argument list; use '&TestClass::Width' to create a pointer to member

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this