Почему я могу предотвратить неявные преобразования для примитивов, но не для определяемых пользователем типов?

Стандарты High Integrity C++ предполагают, что аргументы rvalue для функций могут быть удалены, что предотвращает неявные преобразования.

http://www.codingstandard.com/rule/8-3-4-define-delete-functions-with-parameters-of-type-rvalue-reference-to-const/

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

struct A { };

struct B { B(const A& ) {} };

template <class T>
void foo(const T&&) = delete;  // 1 - deleted rvalue overload. const intentional.

void foo(B) {}                 // 2

void foo(int) {}               // 3

int main(int argc, char* argv[])
{
  A a;
  foo(a);   // This resolves to 2
  foo(3.3); // This resolves to 1
  foo(2);   // This resolves to 3 (as expected).
}       

Почему удаленная перегрузка rvalue предотвращает неявное преобразование в int, но не из одного определяемого пользователем типа в другой?


person jbcoe    schedule 15.05.2017    source источник
comment
ММ указал, что мой пример кода сбивает с толку.   -  person jbcoe    schedule 16.05.2017
comment
Вторая половина этого предложения также неверна. Стандарты High Integrity C++ предполагают, что аргументы rvalue для функций могут быть удалены, что предотвратит неявные преобразования. Перегрузки RValue могут быть удалены, но HIPCC не предполагает, что это может предотвратить неявные преобразования.   -  person jbcoe    schedule 16.05.2017


Ответы (3)


В вашем коде нет разницы в обработке между пользовательскими типами и примитивными типами. Разница между поведением этих двух строк:

foo(a);
foo(3.3);

заключается в том, что a — это lvalue, а 3.3 — это rvalue. Аргумент rvalue соответствует вашей перегрузке 1 (которая принимает только rvalue), аргумент lvalue — нет.

Если вы попытаетесь вызвать foo<A> с аргументом rvalue, он также будет соответствовать 1 и потерпит неудачу, например. foo(A{});.

person M.M    schedule 15.05.2017
comment
Ты прав. Я немного озадачен этим. Почему неявное преобразование также не соответствует перегрузке rvalue? (Возможно, это стоит задать отдельным вопросом?) - person jbcoe; 16.05.2017
comment
@jbcoe При разрешении перегрузки шаблоны создаются в соответствии с предоставленными аргументами. Затем среди экземпляров и функций, не являющихся шаблонами, происходит разрешение перегрузки (что включает в себя рассмотрение неявных последовательностей преобразования и т. д.). - person M.M; 16.05.2017
comment
Например, в foo(a) набором перегрузки являются foo(B) и foo(int) — не было возможного создания экземпляра шаблона для a в качестве аргумента. Но для foo(3.3) набором перегрузки являются foo<double>(const double&&), foo(B) и foo(int). Процесс ранжирования выбирает первый из этих трех, потому что привязка прямой ссылки ранжируется перед преобразованием из числа с плавающей запятой в целое число. - person M.M; 16.05.2017
comment
опубликован дополнительный вопрос: stackoverflow.com/questions/43995946/ - person jbcoe; 16.05.2017

Стандарты High Integrity C++ предполагают, что аргументы rvalue для функций могут быть удалены, что предотвращает неявные преобразования.

Нет, только перегрузка переадресация ссылки отключает ICS (неявное преобразование Sequence) для всех других перегрузок в наборе перегрузок. Сделайте его ссылкой для пересылки и увидите, что ICS отключен (Coliru Link)

template <class T>
void foo(const T&&) = delete;  // introduces a qualification match

Приведенный выше код добавляет к перегрузке соответствие квалификации. Таким образом, ICS все еще в игре.

Ошибка foo(3.3) связана с тем, что 3.3 является prvalue типа double, который лучше соответствует перегрузке rvalue, чем преобразование в int. Поскольку соответствие квалификации ранжируется лучше, чем соответствие конверсии

person WhiZTiM    schedule 15.05.2017
comment
Объявление void foo(const B&&) = delete; предотвращает неявное преобразование. Однако мой пример кода сбивает с толку. Я опубликую дополнительный вопрос. - person jbcoe; 16.05.2017
comment
@jbcoe, да, удаление перегрузки const rvalue для определенного типа также отключает ICS для этого типа. Но удаление перегрузки forwarding reference отключает ICS для всего набора перегрузок. - person WhiZTiM; 16.05.2017
comment
Спасибо. Почему поведение функций и шаблонов функций отличается? - person jbcoe; 16.05.2017
comment
Удаление ссылки переадресации слишком агрессивно, поскольку предотвращает вызов с неконстантной ссылкой lvalue. - person jbcoe; 16.05.2017
comment
опубликован дополнительный вопрос: stackoverflow.com/questions/43995946/ - person jbcoe; 16.05.2017
comment
@jbcoe, шаблоны функций не преобразуют и не вызывают никаких операций преобразования, в лучшем случае они применяют квалификацию типа. функции, с другой стороны, могут вызывать конверсии. И разрешение перегрузки учитывает это при выборе окончательной специализации функции, используемой для конкретного вызова с его аргументом. - person WhiZTiM; 16.05.2017

Возможны 3 перегрузки

  • 1 является жизнеспособным.
  • 2 является жизнеспособным
  • 3 нет

2 лучше соответствует (шаблон (не точное совпадение) по сравнению с обычным методом (с одним преобразованием, определяемым пользователем)).

Вы можете посмотреть на http://en.cppreference.com/w/cpp/language/overload_resolution, чтобы увидеть полный набор необходимых правил

person Jarod42    schedule 15.05.2017