Оператор преобразования против удаленного конструктора - PullRequest
0 голосов
/ 29 июня 2018

Пожалуйста, смотрите следующий код:

struct X;

struct Y {
  Y() {}
  Y(X&) = delete;
};

struct X {
  X() {}
  operator Y() {
    return{};
  }
};

int main() {
  X x;
  static_cast<Y>(x);
}

Здесь конструктор Y, принимающий X, явно удаляется, в то время как X имеет оператор преобразования в Y. Из этих двух противоречащих друг другу, кажется, =delete всегда побеждает; Я тестировал на некоторых последних версиях GCC, Clang и VC ++.

Вопрос: это "правильное" поведение? Я думал, что между конструктором преобразования и оператором преобразования нет особого приоритета, поэтому приведенный выше код должен выдавать ошибку неоднозначности разрешения перегрузки. Но это не так. Он жалуется на использование удаленной функции. Это из-за гарантированной копии?

Я гуглил и нашел Конструктор преобразования и оператор преобразования: приоритет . В этом вопросе был выбран оператор преобразования, потому что он лучше соответствовал наличию const в конструкторе преобразования. Однако в моем случае замена Y(X&) на Y(X const&) ничего не изменила.


На самом деле, я хочу иметь следующую ситуацию:

X x;
Y y1(x);                  // Error
Y y2 = static_cast<Y>(x); // OK

Да, это можно назвать глупым, но на самом деле есть встроенные типы, которые ведут себя именно так: подставьте X <- int&, Y <- int&&. Невозможность создать пользовательский тип, который точно имитирует встроенный ссылочный тип, кажется действительно ужасно отсутствующей частью в текущем C ++ ...

Ответы [ 2 ]

0 голосов
/ 29 июня 2018

Вопрос: это "правильное" поведение? Я думал, что нет особого приоритета между конструктором преобразования и оператором преобразования [...]

Это не совсем верно. Вы смотрите на код как есть:

struct Y {
  Y() {}
  Y(X&) = delete;
};

Но на самом деле там есть нечто большее. Для компилятора Y выглядит так:

struct Y {
  Y() {}
  Y(X&) = delete;

  Y(Y&&) = default;
  Y(Y const&) = default;
};

Выбор здесь не между Y(X&) и X::operator Y(). Выбор в основном между Y(X&) и Y(Y&&). И первое совпадение лучше, чем второе (независимо от того, как вы упомянули в вопросе, это X& или X const& в качестве параметра). Но он удален, поэтому преобразование является неправильным.

<ч />

Если бы мы инициализировали копию вместо прямой инициализации:

Y y = x;

Тогда оба будут одинаково жизнеспособными (и, следовательно, неоднозначными). И да, вы действительно хотите, чтобы это было неоднозначным. = delete не удаляет из набора кандидатов!

При изменении конструктора с Y(X&) на Y(X const&) предпочтительна функция преобразования.

<ч />

Да, это можно назвать глупым, но на самом деле есть встроенные типы, которые ведут себя так: заменитель X <- int&, Y <- int&&

Да, но в исходном примере X и Y отличаются типами . Здесь они представляют разные категории значений одного и того же типа. Причина, по которой этот новый пример работает или не работает, совершенно иная:

X x;
Y y1(x);                  // Error
Y y2 = static_cast<Y>(x); // OK

действительно:

int& x = ...;
int&& y(x);                       // error, can't bind rvalue reference to lvalue
int&& y2 = static_cast<int&&>(x); // ok. this is exactly std::move(x)

Ссылочная привязка к ссылочно-совместимым типам - не тот же вопрос, что и приоритет преобразования.

0 голосов
/ 29 июня 2018

Из стандарта 11.6.17.6.2

если инициализация является прямой инициализацией, или если это инициализация копирования, где cv-неквалифицированная версия типа источника является тем же классом или производным классом класса назначения, конструкторы считаются.

Тогда стандарт говорит нам, что (11.6.16)

Инициализация, которая происходит в формах [...], а также в new выражениях (8.5.2.4), static_cast выражениях (8.5.1.9), преобразованиях типов функциональных обозначений (8.5.1.3), mem -initializer (15.6.2), и форма условия в скобках-init-list называется прямой инициализацией.

В вашем примере временная инициализация выполняется с помощью static_cast, поэтому компилятору разрешено использовать конструкторы только из-за прямой инициализации, поэтому вы получаете ошибку.

...