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

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

У меня есть код вроде следующего:

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]. Клэнг дает ошибку для обоих, и, по-видимому, MSVC дает ошибку ни на одном из них. Кто прав?

4b9b3361

Ответ 1

TL;DR

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

Спойлер

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


Прервать его с текущий рабочий проект С++

Инициализаторы [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 - тип, который является intialised, в обоих случаях это 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

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

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

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

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

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

Per [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 больше cv-qual, чем 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, то есть пользовательские последовательности конверсий.

Ранжирование последовательностей преобразования over.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