Почему неявное преобразование не является неоднозначным для не примитивных типов? - PullRequest
16 голосов
/ 19 марта 2019

Имеется простой шаблон класса с несколькими неявными функциями преобразования (неявный конструктор и оператор преобразования), как в следующем примере:

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?
    }
}

Операторы сравнения, включающие примитивные типы (bool, int), как и ожидалось, неоднозначны - компилятор не знает, должен ли он использовать оператор преобразования для преобразования экземпляра левого класса шаблона в тип примитива или использовать конструктор преобразования для преобразования правого типа примитива в ожидаемыйЭкземпляр шаблона класса.

Однако последнее сравнение, включающее простое struct, не является неоднозначным.Зачем?Какая функция преобразования будет использоваться?

Протестировано с компилятором msvc 15.9.7.

Ответы [ 2 ]

19 голосов
/ 19 марта 2019

Согласно [over.binary] / 1

Таким образом, для любого бинарного оператора @, x@y можно интерпретировать либо x.operator@(y), либо operator@(x,y).

Согласно этому правилу, в случае e == f компилятор может интерпретировать его только как e.operator==(f), а не f.operator==(e). Так что нет никакой двусмысленности; operator==, который вы определили как член Bar, просто не подходит для разрешения перегрузки.

В случае a == b и c == d встроенный кандидат operator==(int, int) (см. [Over.built] / 13) конкурирует с operator==, определенным как член Foo<T>.

5 голосов
/ 19 марта 2019

Операторские перегрузки, реализованные как функции-члены, не допускают неявного преобразования их левого операнда, являющегося объектом, для которого они вызываются.

Это всегда помогает записать явный вызовперегрузка оператора, чтобы лучше понять, что именно он делает:

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

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

Это нельзя спутать с оператором сравнения в Bar, потому что это потребует неявного преобразования левой части, котораяневозможно.

Вы можете вызвать неоднозначность, аналогичную той, которую вы видите со встроенными типами при определении Bar и его операторе сравнения, например:

struct Bar { bool m; };

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

Это хорошопродемонстрировано и объяснено в Скотт Мейерс Эффективный C ++ , пункт 24.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...