1

I am working with some legacy code, where one of the previous developers created a Guid (Global Unique IDentifier) class that used Microsoft’s GUID struct as a member variable (member "MS" below). To allow easy conversion between the two, the following two conversion/casting operators were defined in Guid.h:

/// Returns a GUID structure
operator GUID() const
{
    return MS;
}

/// Returns a reference to a GUID structure
operator GUID&()
{
    return MS;
}

On moving the codebase to a VS2019 build, I get the following compiler error:

error C2593: 'operator =' is ambiguous C:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\shared\guiddef.h(27): note: could be '_GUID &_GUID::operator =(_GUID &&)' C:\Program Files (x86)\Windows Kits\10\include\10.0.18362.0\shared\guiddef.h(27): note: or
'_GUID &_GUID::operator =(const _GUID &)'

I’m assuming either the compiler has become more strict (since VS2013) or Microsoft updated their GUID definition with a second overloaded GUID assignment operator. An example of where this error is hit is as follows:

void GuidExample(Guid initial)
{
    GUID myGUID = initial;
}

My understanding is that during the assignment, the compiler will try to convert the Guid to a GUID using one of the two conversion operators we’ve supplied. However, it doesn’t know which one of our conversion operators to use, and therefore doesn’t know which assignment operator to use.

If I comment out either of the two conversion operators, I don’t get any compiler errors. Return by reference will allow access to GUID MS, which is OK because it is a public member anyway. So If I have to go with one definition of the conversion operation, I will go with the reference version.

However my question is, is there a way to keep both definitions and avoid the ambiguity?

UPDATE: Minimum reproducible example. This will build in VS2013 and will not in VS2019, showing the "'operator =' is ambiguous" error I noted earlier.

#include <guiddef.h>

class Guid
{
public:
    union
    {
        char Data[16];
        GUID MS;
        struct
        {
            int Q1;
            int Q2;
        };
    };

Guid()
{
    Q1 = 0;
    Q2 = 0;
}

/// Returns a GUID structure
operator GUID() const
{
    return MS;
}

/// Returns a reference to a GUID structure
operator GUID& ()
{
    return MS;
}
};


GUID winGUID;

void testAssign(Guid myGuid)
{
    winGUID = myGuid; //This causes ambiguity
    GUID anotherWinGUID = myGuid; //This does not
}

int  main()
{
    Guid a;
    testAssign(a);
    return 0;
}
Nimo
  • 324
  • 2
  • 15
  • 1
    Why do you need both coversions? Converting to a non-const reference is a little odd – Alan Birtles Aug 27 '20 at 10:16
  • It complains about `operator =`. Is any defined? – vahancho Aug 27 '20 at 10:16
  • 1
    This issue reminds me of the of the restriction on member function qualifiers: For a given function you can have both `const` and non-const, or `&`, `&&`, `const &` and `const &&` - but not both. This is because having both will be ambiguous, and redundant. – Benny K Aug 27 '20 at 10:32
  • @AlanBirtles it does seem odd and I'm going through the many use cases. For now I'm assuming it was done for a good reason. – Nimo Aug 27 '20 at 10:46
  • @vahancho Yes, the two definitions are listed in the compiler output I posted. – Nimo Aug 27 '20 at 10:48
  • @Nimo, I see, so the compiler doesn't know which one to call. You should hint. What if you write: `GUID myGUID = std::move(initial);`? – vahancho Aug 27 '20 at 10:51
  • @nop666: You missed the `const` in that example. That is critically important in overload resolution. – MSalters Aug 27 '20 at 11:43
  • @MSalters My mistake. [Here](https://godbolt.org/z/boh4j8) is the new benchmark. 3rd case triggers a different behavior on clang/gcc. Any clue why? – nop666 Aug 27 '20 at 12:06
  • By the way, I just posted [this](https://stackoverflow.com/q/63618919/12978013) to have insights on the behavior I encountered during the tests... – nop666 Aug 27 '20 at 15:13

1 Answers1

2

The error tells you that the compiler cannot choose between copy-assignment and move-assignment. So you're right, there are now two overloads of the assignment operator, and they're just the usual ones.

The compiler might be more strict. Visual C++ has been tightening the rules with /permissive-, starting with VS2015 Update 3.

The fix is probably straightforward:

/// Returns a GUID structure
operator GUID() const
{
    return MS;
}
/// Returns a reference to a GUID structure
operator GUID&() &  // << the trailing & is a reference-qualification.
{
    return MS;
}

The idea here is that you don't want a GUID& reference to a temporary Guid object which might not even survive after the call to operator GUID&().

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Have given this a try and the compiler is still unhappy unfortunately, same output as my original post. – Nimo Aug 27 '20 at 12:57
  • @Nimo Could you build a [minimum reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) please. [Here](https://godbolt.org/z/boh4j8) is a thing I started to try to understand your issue. – nop666 Aug 27 '20 at 14:03
  • @nop666 will let you know. I appreciate the effort. – Nimo Aug 27 '20 at 14:06
  • @nop666 just an update. I've made a basic example that DOES compile, although wasn't supposed to. So now need to track down the difference between the two. – Nimo Aug 28 '20 at 08:38