多个转换运算符引起赋值运算符的歧义

问题描述

我正在处理一些旧代码,以前的一位开发人员在其中创建了一个Guid(全局唯一IDentifier)类,该类使用Microsoft的GUID结构作为成员变量(下面的成员“ MS”)。为了便于在两者之间进行转换,在Guid.h中定义了以下两个转换/广播运算符:

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

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

代码库移至VS2019版本时,出现以下编译器错误

错误C2593:'运算符='是不明确的C:\ Program Files(x86)\ Windows 套件\ 10 \ include \ 10.0.18362.0 \ shared \ guiddef.h(27):注意:可能是 '_GUID&_GUID :: operator = {_ GUID &&)'C:\ Program文件(x86)\ Windows 套件\ 10 \ include \ 10.0.18362.0 \ shared \ guiddef.h(27):注意:或
'_GUID&_GUID :: operator =(const _GUID&)'

我假设要么编译器变得更严格(自VS2013开始),要么微软使用第二个重载的GUID赋值运算符更新了其GUID定义。发生此错误的示例如下:

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

我的理解是,在分配过程中,编译器将尝试使用我们提供的两个转换运算符之一将Guid转换为GUID。但是,它不知道要使用哪个转换运算符,因此也不知道要使用哪个赋值运算符。

如果我注释掉这两个转换运算符中的任何一个,则不会出现任何编译器错误。通过引用返回将允许访问GUID MS,这是可以的,因为它仍然是公共成员。因此,如果必须对转换操作进行一个定义,则将使用参考版本。

但是我的问题是,有没有办法既保留两个定义又避免歧义?

更新:最小的可复制示例。这将在VS2013中构建,而不会在VS2019中构建,这表明我之前提到的“'operator ='是模糊的”错误

#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;
}

解决方法

该错误告诉您编译器无法在复制分配和移动分配之间进行选择。所以你是对的,赋值运算符现在有两个重载,它们只是通常的重载。

编译器可能更严格。从VS2015 Update 3开始,Visual C ++一直使用/permissive-加强规则。

修复可能很简单:

/// 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;
}

这里的想法是,您不希望对临时GUID&对象的Guid引用在调用operator GUID&()之后可能甚至无法生存。