NGWS SDK Documentation  

This is preliminary documentation and subject to change.
To comment on this topic, please send us email at ngwssdk@microsoft.com. Thanks!

Technote: Improved Conformance to ANSI C++

Because of improved C++ conformance, some constructs that compiled in older versions of Visual C++ now produce errors. The errors affected are:

C2434 C2440 C2446 C2553 C2585 C2592
C2664 C2665 C2666 C2667 C2668 C2674
C2675 C2676 C2677 C2678 C2679  

Overload Resolution Changes

Visual C++ versions 4.0 and later bring overload resolution rules closer to the C++ standard. This allows some code to work that did not work previously, but can result in ambiguities in code that compiled cleanly with earlier versions of Visual C++.

Dereferencing a Reference is an Identity Conversion

Versions of Microsoft Visual C++ prior to 4.0 did not consider dereferencing a reference, or binding to a reference, to be an exact match. Visual C++ versions 4.0 and later considered the conversions trivial, as specified by the Annotated C++ Reference Manual.

Example

void foo(const int);
void foo(int&);

void bar(void)
{
    int i;
    int &ri = i;
    foo(i);  // Accepted by VC2.0, VC1.x, rejected by VC4.0, error C2668
    foo(ri);  // Accepted by VC2.0, VC1.x, rejected by VC4.0, error C2668
}

User-Defined Conversions (UDCs) are Selected by 'this' Pointer Types

Versions of Visual C++ prior to 4.0 selected a user-defined conversion to call by determining the required conversion from the UDC result type to the required type, ignoring the conversion required for the this pointer of the UDC. Later versions select a UDC based on the quality of match on the this pointer. If two conversions have identical this pointers, a Microsoft extension uses the quality of the conversion from the UDC result to the target type to determine the UDC. (This extension is not disabled by /Za.)

The new behavior catches some errors not previously detected.

Example

struct C {
    // some class
};

struct D {
    // A class which wraps a C
    C myC;

    // Two user-defined conversion functions, one for 
    //  using myC, and the other for modifying it
    operator C () { return myC; }   // conversion #1
    operator C& () { return myC; }   // conversion #2
};

// A function that takes a C
void func1(C);

// A function that has a D
void func2()
{
    D aD;

    func1(D);  // Error diagnosed in VC4.0: error C2664
}

In this example, the user-defined conversion is ambiguous because the conversions for the this pointers are identical and the the UDC results have identical conversions. The following code (which follows the previous code) shows another error:

// A function that wants to modify a C
void func3(C&);

// Some function that gets a D&
void func4(const D& aD)
{
    func3(aD);  // Error diagnosed in VC4.0: error C2664
}

This time, neither UDC can be called. The D object is const, but neither UDC takes a const this pointer.

To correct both these problems, rewrite conversion #1 as follows:

operator C () const { return myC; }  // conversion #1a

or:

operator const C& () const { return myC; }  // conversion #1b

Both conversions take a const this pointer and can be called for const objects. Conversion #1b returns a reference to a const object, avoiding copy construction, but still ensuring that the original object cannot be modified.

An additional, more subtle effect of this change is that user-defined conversions can silently change behavior:

Example

void foo(int);

struct C {
    operator int () const;
    operator long ();
};

void main(void)
{
    C aC;
    const C acC;

    foo(acC);  // calls "operator int() const"
    foo(aC);   // calls "operator int() const" in VC2.0, 
               // but "operator long()" in VC4.0
}