Подтвердить что ты не робот

Почему я могу использовать operator =, но не operator == с С++ 11 brace-initializers?

См. этот пример:

struct Foo
{
    int a;
    int b;

    bool operator == (const Foo & x)
    {
        return a == x.a && b == x.b;
    }
};

int main ()
{
    Foo a;

    a = {1, 2};

    if (a == {1, 2}) // error: expected primary-expression before ‘{’ token
    {
    }
}

Строка a={1,2} прекрасна. Скобки преобразуются в Foo для соответствия типу аргумента неявного метода operator=. Он по-прежнему работает, если operator= определяется пользователем.

Ошибки строки if (a=={1,2}}), как указано.

Почему выражение {1,2} не конвертируется в Foo для соответствия пользовательскому методу operator==?

4b9b3361

Ответ 1

Инициализация списка не может использоваться в качестве аргумента для оператора в общем случае. В пункте 8.5.4/1 стандарта С++ 11:

[...] Можно использовать инициализацию списка

- как инициализатор в определении переменной (8.5)

- как инициализатор в новом выражении (5.3.4)

- в операторе return (6.6.3)

- как инициализатор диапазона (6.5)

- как аргумент функции (5.2.2)

- как индекс (5.2.1)

- как аргумент вызова конструктора (8.5, 5.2.3)

- как инициализатор для нестатического элемента данных (9.2)

- в mem-инициализаторе (12.6.2)

- в правой части присваивания (5.17)

Последний элемент объясняет, почему инициализация списка разрешена в правой части operator =, даже если она вообще не допускается для произвольного оператора.

Однако из-за пятого элемента выше он может использоваться в качестве аргумента для обычного вызова функции, таким образом:

if (a.operator == ({1, 2}))

Ответ 2

Он просто не поддерживается.

Списки инициализаторов явно определены как действительные при инициализации ([C++11: 8.5.4]) и присваивания:

[C++11: 5.17/9]: В правой части

может появиться список с привязкой к init-init.
  • присвоение скаляру, и в этом случае список инициализаторов должен иметь не более одного элемента. Значение x={v}, где T является скалярным типом выражения x, является значением x=T(v), за исключением того, что сужение преобразования (8.5.4) не допускается. Значение x={} составляет x=T().
  • назначение, определенное пользовательским оператором присваивания, и в этом случае список инициализаторов передается как аргумент функции оператора.

Нет стандартных формулировок, позволяющих допускать другие, произвольные случаи.

Если бы это было разрешено, в этом примере тип {1,2} был бы довольно неоднозначным. Это будет сложная языковая функция для реализации.

Ответ 3

Требуется явное литье.

if (a == (Foo){1, 2})
{
}

Ответ 4

Когда вы используете выражение, содержащее определяемый пользователем тип a и == operator Вызывается перегруженная функция оператора, которая в соответствии с заданным определением требует аргумента ссылки объекта класса Foo

поэтому ваше выражение должно быть как == b

где b - объект класса Foo

Здесь с этим выражением вы сможете сравнить данные из элементов b и a и, следовательно, знать, равны они или нет.

Ответ 5

Нет реальной причины.

С++ является результатом усилий комитета, поэтому иногда странные, но преднамеренные решения могут ускользнуть из-за сложной политической/социологической динамики.

Синтаксис С++ сложный. Очень сложно. Почти невероятно сложно. Существуют правила, даже если вы можете анализировать эту произвольно длинную последовательность токенов как то или это, то это так.

Прошло много лет для компиляторов, чтобы просто согласиться с тем, что такое С++, а что нет.

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

MyClass x = {...};
MyClass y; y = {...};

будет обрабатываться по-разному, поэтому для присвоения требуется специальное положение для разрешения синтаксиса.

С технической точки зрения я не вижу, какие проблемы допускают его для других операторов, а с другой стороны, если есть проблемы (например, для перегрузки, создания экземпляра шаблона и т.д.), я не вижу как назначение может надеяться избежать их.

ИЗМЕНИТЬ

g++ позволяет использовать не только строгие operator=, но также operator+=, operator-= и подобное "расширенное назначение". Возможно, логические проблемы возникают только в том случае, если вы разрешаете перегрузку без участия (которые запрещены для присваивания и расширенные операторы присваивания).