Visual C++ is so Liberal

22:53
Tue
31
Jan 2012

Visual C++ is so Liberal

Here is an issue in C++ code I've came across some time ago and recently I've found again in some other code. This code is invalid according to language standard, still it compiles in Visual C++/Visual Studio and works correctly. Can you see what's wrong with it?

class Class1
{
public:
    int m_Number;
    Class1( int number ) : m_Number( number ) { }
};

void Function1( Class1& obj1 )
{
    obj1.m_Number = 2;
}

int main()
{
    Function1( Class1( 1 ) );
}

The problem is that we pass a temporary object of Class1 to the Function1, while this function takes object by reference, not by reference to const. Such R-value shouldn't be converted to non-const reference, or else we could modify the temporary object - which we actually do inside the function.

Visual C++ 2008 and 2010 with default project options compiles this without any warning. Setting Warning Level to 4 (/W4) generates a warning:

warning C4239: nonstandard extension used : 'argument' : conversion from 'Class1' to 'Class1 &'

Using Disable Language Extensions (/Za) makes it an error:

error C2664: 'Function1' : cannot convert parameter 1 from 'Class1' to 'Class1 &' A non-const reference may only be bound to an lvalue

It means we are dealing with a nonstandard Microsoft extension here. GCC refuses to compile this code, even with standard options:

error: invalid initialization of non-const reference of type 'Class1&' from an rvalue of type 'Class1' error: in passing argument 1 of 'void Function1(Class1&)'

Another story: Today I've learned that C++ standard doesn't have forward declarations of enums. I used it for long time and now I know it's another nonstandard Microsoft extension.

My conclusion is that programming in C++ using Visual C++ is like programming in DirectX using NVIDIA graphics cards: the platform is so liberal that your code may work even if you do something invalid. It also means that to use portable libraries instead of WinAPI is not enought to write code portable from Windows to Linux and other platform. You should also check if your code is accepted by other compilers. Increasing warning level in project options can also help with that, just like using Debug Version of Direct3D in DiectX Control Panel and observing debugger Output to find possible problems in calls to Direct3D API. It's better to ensure early that your code is valid instead of later complain that alternative (GCC) compiler or alternative (AMD) GPU driver causes problems.

On the other hand I believe that platform independence and strict C++ standard correctness is not a great value in itself. If you know your code is supposed to just work under Windows and be compiled in Visual C++, why not make use of available extensions and rely on specific compiler behavior? It can be convenient, while maintaining code that have to work with different compilers and platforms is a lot of additional work, possibly unnecesary.

Comments (5) | Tags: c++ visual studio

Comments

Jim Tilander
2012-02-01 06:33:31
I blame it on windows.h being retarded and the fact that the MS compiler's job is to compile windows programs -- i.e. include windows.h ... and off you go into the rabbit hole.

You can turn on the stricter mode, but will get into trouble with precompiled headers since the options now doesn't match, and one of the reasons why you had the precompiled headers in the first place is to have windows.h there! It's an evil circle. I've in the past just given up and tried to teach programmers instead the evils of the MS compiler and the goodness of gcc :)
Xion
2012-02-01 12:14:56
> This code is invalid according to language standard, still it compiles in Visual C++/Visual Studio and works correctly.

What do you mean by "works correctly" here? As far as I can tell, this code produces no actual effects, and the compiler could just do away with Function1 and Class1 entirely. Short of inspecting the assembly, you wouldn't notice the difference. The fact that VC++ does not make a fuss looks more like un upside here. Of course, if it happily ignored something like:

someGlobalPointer = &obj;

inside Function1, then it would warrant complaining about VC++ being too lenient. That in your case it only produces a warning at /W4 seems quite reasonable to me, given than "nothing" is really going on here, i.e. the warning is about superfluous code.
Syriusz
2012-02-02 09:41:52
gcc is so liberal ;D:

int array[0];
int main() { return 0; }

Visual C++ does't allow such a non standard code to pass, when the gcc even doesn't rise the warning ;P
Reg
2012-02-02 18:01:23
Xion: Right, this code does nothing. Nevertheless generally the extension I described can be used. I once coded a class for string formatting. Its usage was:

std::string str = Format("Data: %d %s") % number % str;

Format class has operator% and operator std::string (conversion).

The operator% inside this class modified the temporary non-const object in place. I then tried to port my library to Linux using GCC. That was the first time I learned that it's a nonstandard Microsoft extension.
Xion
2012-02-04 19:36:58
Interesting. I'd say that it's still a "no harm done" situation if that temporary object was a std::string which was subsequently returned as a result (and thus copied).

Have you maybe researched the rationale behind VC++ having this extension at all? I wouldn't be all that surprised if it could be proven to not produce any harmful/undefined behavior, although with C++ you can never be sure ;-)

Post comment

Nick *
Your name or nickname
URL
URL to your homepage or e-mail address (optional)
Text *
Content of your comment
Calculate *
(* - required field)
STAT NO AD [Stat] [Admin] [STAT NO AD] [pub] [Mirror] Copyright © 2004-2012 Adam Sawicki
Copyright © 2004-2012 Adam Sawicki