为什么非原始类型的隐式转换没有歧义?

Cyb*_*ran 16 c++ templates ambiguous implicit-conversion c++17

给定一个简单的类模板,其中包含多个隐式转换函数(非显式构造函数和转换运算符),如以下示例所示:

template<class T>
class Foo
{
private:
    T m_value;

public:
    Foo();

    Foo(const T& value):
        m_value(value)
    {
    }

    operator T() const {
        return m_value;
    }

    bool operator==(const Foo<T>& other) const {
        return m_value == other.m_value;
    }
};

struct Bar
{
    bool m;

    bool operator==(const Bar& other) const {
        return false;
    }
};

int main(int argc, char *argv[])
{
    Foo<bool> a (true);
    bool b = false;
    if(a == b) {
        // This is ambiguous
    }

    Foo<int> c (1);
    int d = 2;
    if(c == d) {
        // This is ambiguous
    }

    Foo<Bar> e (Bar{true});
    Bar f = {false};
    if(e == f) {
        // This is not ambiguous. Why?
    }
}
Run Code Online (Sandbox Code Playgroud)

如预期的那样bool,涉及基本类型(int)的比较运算符是模棱两可的-编译器不知道应使用转换运算符将左侧模板类实例转换为原始类型,还是使用转换构造函数将右侧运算符转换为原始类型将原始类型传递给期望的类模板实例。

但是,最后一个涉及simple的比较struct并不是模棱两可的。为什么?将使用哪个转换功能?

使用编译器msvc 15.9.7测试。

Bri*_*ian 19

根据[over.binary] / 1

因此,对于任何二进制运算符@x@y都可以解释为x.operator@(y)operator@(x,y)

根据此规则,在情况下e == f,编译器只能将其解释为e.operator==(f),而不能解释为f.operator==(e)。因此没有歧义。在operator==你定义为成员Bar根本就不是为重载的候选人。

在的情况下a == bc == d,内置的候选operator==(int, int)(见[over.built] / 13)与所述竞速赛operator==定义为一个构件Foo<T>


lub*_*bgr 5

实现为成员函数的运算符重载不允许对其左操作数进行隐式转换,左操作数是调用它们的对象。

它总是有助于写出运算符重载的显式调用,以更好地准确了解其作用:

Foo<Bar> e (Bar{true});
Bar f = {false};

// Pretty explicit: call the member function Foo<Bar>::operator==
if(e.operator ==(f)) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

不能将它与中的比较运算符混淆Bar,因为这将需要左侧的隐式转换,这是不可能的。

您可以触发类似于您在定义Bar及其比较运算符时看到的内置类型的歧义,如下所示:

struct Bar { bool m; };

// A free function allows conversion, this will be ambiguous:
bool operator==(const Bar&, const Bar&)
{
   return false;
}
Run Code Online (Sandbox Code Playgroud)

斯科特·迈耶斯(Scott Meyers)的《有效的C ++》第24条很好地说明了这一点。