Какие из этих преобразований должны быть неоднозначными?

У меня есть код, подобный следующему:

class bar;

class foo
{
public:
    operator bar() const;
};

class bar
{
public:
    bar(const foo& foo);
};

void baz() {
    foo f;
    bar b = f;   // [1]

    const foo f2;
    bar b2 = f2; // [2]
}

GCC выдает ошибку в [2], но не [1]. Clang выдает ошибку на обоих, и, очевидно, MSVC выдает ошибку ни на одном. Кто прав?

1 ответ

Решение

ТЛ; др

Неоднозначность. (Кроме того, если вы останавливаетесь на TL; Dr, то language-lawyer тег не может быть вашей чашкой чая. ^_^)

Спойлер

У обоих кандидатов есть один const foo& параметр, который одинаково связывается с const foo или же foo аргумент. Не появляются другие правила, которые бы предпочли ту или иную функцию.


Разбив его с текущим рабочим проектом C++

Инициализаторы [dcl.init]

В обоих случаях

  • мы выполняем инициализацию копирования ( [dcl.init] / 15)
  • тип назначения является типом класса ( [dcl.init] /17.6)
    • (X) выражение инициализатора не является prvalue ( [dcl.init] /17.6.1)
    • (X) тип источника не совпадает или не является производным от типа назначения ( [dcl.init] /17.6.2)
    • Пользовательские последовательности преобразования перечисляются из [over.match.copy] и лучше всего выбираются по разрешению перегрузки.

Инициализация копии класса с помощью пользовательского преобразования [over.match.copy]

T тип инициализируется, в обоих случаях это bar, S тип выражения инициализатора, в двух случаях foo а также const foo соответственно.

  • преобразование конструкторов T являются кандидатами ( [over.match.copy] /1.1)
    • bar::bar(const foo& foo); является кандидатом
  • тип выражения инициализатора _cv_ S поэтому рассматриваются неявные функции преобразования: ( [over.match.copy] /1.2)
    • foo::operator bar() const не спрятан внутри foo или в пределах const foo и дает bar который так же, как T и, следовательно, является кандидатом.

Таким образом, наш список кандидатов одинаков в обоих случаях:

  • bar::bar(const foo& foo)
  • foo::operator bar() const

В обоих случаях у нас есть пользовательское преобразование, состоящее из:

  1. Стандартное преобразование типа источника в определяемый пользователем аргумент преобразования
  2. Определяемое пользователем преобразование (одна из двух вышеуказанных функций) в тип результата
  3. Стандартное преобразование типа результата в целевой тип

Если мы выберем конструктор, то "тип результата" будет "предварительным значением cv-неквалифицированной версии целевого типа, чей результирующий объект инициализируется конструктором" ( [dcl.init] /17.6.3), поэтому для обоих кандидатов функции, второе Стандартное Преобразование - Идентичность (bar -> bar).

Разрешение перегрузки [over.match]

Подмножество жизнеспособных функций-кандидатов [over.match.viable]

Согласно [dcl.init] /17.6.3, выражение инициализатора будет аргументом для выбранного вызова, в двух случаях foo а также const foo соответственно.

bar::bar(const foo& foo)

  • Один аргумент в списке аргументов, ровно один параметр. ( [over.match.viable] /2.1)
  • Нет связанных ограничений ( [over.match.viable] / 3)
  • Последовательность неявного преобразования существует из обоих foo а также const foo в const foo& ( [over.match.viable] / 4)
  • Первоначальным стандартным преобразованием является преобразование идентичности в обоих случаях: [over.best.ics] / 5 => [over.ics.ref] / 1 для привязки прямой ссылки:
    • const foo совместим с обоими foo а также const foo как const более квалифицированным, чем оба const и ничего. [dcl.init.ref] / 4
    • const foo& привязывается непосредственно к lvalue foo и lvalue const foo, [dcl.init.ref]/5
  • жизнеспособный

foo::operator bar() const

  • Один аргумент в списке аргументов, ровно один неявный параметр объекта. ( [over.match.viable] /2.1)
    • Неявный параметр объекта const foo& в обоих случаях ( [over.match.funcs] / 4)
  • Нет связанных ограничений ( [over.match.viable] / 3)
  • Последовательность неявного преобразования существует из обоих foo а также const foo в const foo& ( [over.match.viable] / 4)
  • Первоначальным стандартным преобразованием является преобразование идентичности в обоих случаях, см. Выше.
  • жизнеспособный

Выберите лучшую жизнеспособную функцию [over.best.ics]

И то, и другое - Identity => User Defined Conversion => Identity, т. Е. Определенные пользователем последовательности преобразования.

Ранжирование конверсионных последовательностей через .ics.rank

Можем ли мы установить ранжирование между последовательностями? Только если применяется одно из следующих

  • (X) Не последовательности инициализации списка ( [over.ics.rank] / 3)
  • (X) Не стандартная последовательность конвертации ( [over.ics.rank] /3.2)
  • (X) Эти две последовательности не содержат "одну и ту же пользовательскую функцию преобразования или конструктор или [...] инициализируют один и тот же класс в совокупной инициализации" ( [over.ics.rank] /3.3)

Последовательности преобразования неразличимы, т. Е. Ни лучше, ни хуже

Лучшая жизнеспособная функция over.match.best

Является ли любая из этих функций "лучшей"? Только если применяется одно из следующих

Ни одна из них не является "лучшей" функцией, поэтому вызов плохо сформирован. [over.match.best] / 2

Другие вопросы по тегам