Если первый операнд аддитивного выражения может быть преобразован как в указатель, так и в целое число, какое преобразование выбрано?

Какую функцию преобразования следует вызвать в следующем примере? Почему следует предпочесть одно другому?

struct A
{
  operator int();
  operator int*();
};

A x;
int i = x + 1;

Компилятор выбирает operator int() .. но почему?

Вот некоторые актуальные цитаты из C ++ 03:

От [expr.add]

В качестве дополнения, оба операнда должны иметь арифметический или перечислительный тип, или один операнд должен быть указателем на полностью определенный тип объекта, а другой должен иметь целочисленный или перечисляемый тип.

От [конв.]

выражения с заданным типом будут неявно преобразованы в другие типы в нескольких контекстах:

  • При использовании в качестве операндов операторов. Требования оператора к его операндам диктуют тип назначения.

person willj    schedule 15.07.2013    source источник
comment
Я бы сказал, что это должно быть двусмысленно. Я не думаю, что указатели более особенные.   -  person chris    schedule 16.07.2013
comment
Он вызывает другой, если вы говорите int *i = x + 1;?   -  person John    schedule 16.07.2013
comment
@John, нет, он просто жалуется на int -> int *.   -  person chris    schedule 16.07.2013
comment
Что ж, я быстро просмотрел § 13.3.3.2 (Ранжирование последовательностей неявных преобразований) и не нашел ничего особенного, относящегося к этому.   -  person chris    schedule 16.07.2013


Ответы (1)


Причина такого поведения в том, что встроенный оператор, который принимает указатель в качестве своего левого операнда, принимает объект типа std::ptrdiff_t в качестве своего правого операнда. Это указано в § 13.6 стандарта C ++ 11:

Для каждого cv-квалифицированного или cv-неквалифицированного типа объекта T существуют кандидатные операторные функции вида

T * operator+(T *, std::ptrdiff_t);

[...]

Поскольку 1 имеет тип int, компилятор рассматривает встроенный operator +, который принимает два int, как лучший выбор, потому что для него требуется только (определяемое пользователем) преобразование для первого аргумента.

Если вы предоставили аргумент типа std::ptrdiff_t как правый операнд operator +, вы увидите ожидаемую двусмысленность:

int i = x + static_cast<std::ptrdiff_t>(1); // AMBIGUOUS!

Вот живой пример.

person Andy Prowl    schedule 15.07.2013
comment
Хороший звонок. Где указано, что в этом сценарии следует использовать разрешение перегрузки? - person willj; 16.07.2013
comment
@willj: Я только что выключил свой ноутбук и пишу со своего мобильного, поэтому я не могу сказать точный номер абзаца, но он находится где-то в пункте 13. Я бы посоветовал поискать вхождения оператора в этом пункте, должен не сложно найти - person Andy Prowl; 16.07.2013
comment
13.6 начинается. Функции операторов-кандидатов, которые представляют встроенные операторы, определенные в разделе 5, указаны в этом подразделе. Эти функции-кандидаты участвуют в процессе разрешения перегрузки оператора, как описано в 13.3.1.2, и не используются ни для каких других целей. - person Ben Voigt; 16.07.2013
comment
[over.match.oper] Если какой-либо из операндов имеет тип, являющийся классом или перечислением, может быть объявлена ​​пользовательская операторная функция, реализующая этот оператор, или может потребоваться определенное пользователем преобразование для преобразования операнда в тип что подходит для встроенного оператора. В этом случае разрешение перегрузки используется для определения того, какая операторная функция или встроенный оператор должен быть вызван для реализации оператора. - person willj; 16.07.2013