Почему C ++ 11-удаленные функции участвуют в разрешении перегрузки?

87

Почему в C ++ 11 функции " deleted" участвуют в разрешении перегрузки ?
Почему это полезно? Или, другими словами, почему они скрыты, а не полностью удалены?

пользователь541686
источник
Одновременный вопрос C ++ 11 «Неподвижный» тип .
Olaf Dietsche
См. Обоснование предложения open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2346.htm#delete
Джонатан Уэйкли

Ответы:

114

Половина цели = deleteсинтаксиса состоит в том, чтобы запретить людям вызывать определенные функции с определенными параметрами. В основном это делается для предотвращения неявных преобразований в определенных конкретных сценариях. Чтобы запретить конкретную перегрузку, она должна участвовать в разрешении перегрузки.

Приведенный вами ответ дает вам прекрасный пример:

struct onlydouble {
  onlydouble(std::intmax_t) = delete;
  onlydouble(double);
};

Если deleteудалить функцию полностью, это сделает = deleteсинтаксис эквивалентным следующему:

struct onlydouble2 {
  onlydouble2(double);
};

Вы могли сделать это:

onlydouble2 val(20);

Это законный C ++. Компилятор просматривает все конструкторы; ни один из них напрямую не принимает целочисленный тип. Но один из них может принять это после неявного преобразования. Так это назовем.

onlydouble val(20);

Это не законный C ++. Компилятор просмотрит все конструкторы, включая deleted. Он увидит точное совпадение через std::intmax_t(которое будет точно соответствовать любому целочисленному литералу). Таким образом, компилятор выберет его, а затем немедленно выдаст ошибку, потому что он выбрал функцию deleted.

= deleteозначает «Я запрещаю это», а не просто «Этого не существует». Это гораздо более сильное заявление.

Я спрашивал, почему стандарт C ++ говорит, что = delete означает «Я запрещаю это» вместо «этого не существует»

Это потому, что нам не нужна специальная грамматика, чтобы сказать «этого не существует». Мы получаем это неявно, просто не объявляя конкретное рассматриваемое «this». «Я запрещаю это» представляет собой конструкцию, которая не может быть достигнута без специальной грамматики. Итак, у нас есть специальная грамматика, чтобы сказать: «Я запрещаю это», а не что-то другое.

Единственная функциональность, которую вы получили бы при наличии явной грамматики «это не существует», - это предотвращение того, чтобы кто-то позже объявил ее существованием. И этого недостаточно, чтобы нужна была собственная грамматика.

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

Конструктор копирования - это специальная функция-член. У каждого класса всегда есть конструктор копирования. Так же, как у них всегда есть оператор присваивания копии, конструктор перемещения и т. Д.

Эти функции существуют; вопрос только в том, законно ли им звонить. Если вы попытаетесь сказать, что это = deleteозначает, что они не существуют, тогда спецификация должна будет объяснить, что означает отсутствие функции. Это не та концепция, которую описывает спецификация.

Если вы попытаетесь вызвать функцию, которая еще не была объявлена ​​/ определена, компилятор выдаст ошибку. Но это приведет к ошибке из-за неопределенного идентификатора , а не из-за ошибки «функция не существует» (даже если ваш компилятор сообщает об этом таким образом). Все различные конструкторы вызываются разрешением перегрузки, поэтому их «существование» обрабатывается в этом отношении.

В каждом случае существует либо функция, объявленная через идентификатор, либо конструктор / деструктор (также объявленный через идентификатор, просто идентификатор типа). Перегрузка оператора скрывает идентификатор за синтаксическим сахаром, но он все еще существует.

Спецификация C ++ не может обрабатывать концепцию «несуществующей функции». Он может справиться с несоответствием перегрузки. Он может справиться с неоднозначностью перегрузки. Но он не знает о том, чего там нет. Так = deleteопределяется в терминах гораздо более полезных «попыток назвать это неудачей», чем менее полезных «притвориться, что я никогда не писал эту строку».

И снова перечитайте первую часть. Вы не можете этого сделать с «функции не существует». Это еще одна причина, по которой он определен таким образом: потому что один из основных вариантов использования = deleteсинтаксиса - это возможность заставить пользователя использовать определенные типы параметров, явное приведение и т. Д. В основном, чтобы помешать неявному преобразованию типов.

Ваше предложение этого не сделает.

Николь Болас
источник
1
@Mehrdad: Если вам нужно больше разъяснений по поводу того, почему = delete не работает так, как вы хотите, вам нужно более четко определить точную семантику, которую, по вашему мнению, должен иметь = delete. Должно ли быть «притвориться, что я никогда не писал эту строчку»? Или должно быть что-то другое?
Никол Болас
1
Нет, я имел в виду, что ожидал = deleteиметь в виду «этот элемент не существует», что означало бы, что он не мог участвовать в разрешении перегрузки.
user541686
6
@Mehrdad: И это восходит к моей исходной точке, поэтому я разместил ее: если = deleteимелось в виду «этот член не существует», то первый опубликованный мной пример не смог бы помешать людям передавать целые числа в onlydoubleконструктор , поскольку onlydoubleудаляемая перегрузка не существует . Он не будет участвовать в разрешении перегрузки и, следовательно, не помешает вам передавать целые числа. Это половина = deleteсинтаксиса: возможность сказать: «Вы не можете неявно передать X этой функции».
Никол Болас
3
@Mehrdad: По этой логике, зачем вам вообще это нужно =delete? В конце концов, мы можем сказать «не копируемый», сделав то же самое: объявив конструктор / присваивание копии закрытым. Также обратите внимание, что объявление чего-то частного не делает его невозможным для вызова; код внутри класса все еще может его вызывать. Так что это не то же самое = delete. Нет, = deleteсинтаксис позволяет нам делать то, что раньше было крайне неудобно и непостижимо, гораздо более очевидным и разумным способом.
Никол Болас
2
@Mehrdad: Потому что последнее возможно : это называется «не объявлять». Тогда его не будет. Что касается того, почему нам нужен синтаксис, чтобы скрыть перегрузку, а не злоупотреблять частным ... вы действительно спрашиваете, почему у нас должны быть средства для явного утверждения чего-то, а не злоупотребления какой-либо другой функцией для получения того же эффекта ? Для любого, кто читает код, просто более очевидно, что происходит. Это делает код более понятным для пользователя и упрощает его написание, а также устраняет проблемы в обходном пути. Нам также не нужны лямбды.
Никол Болас
10

Рабочий проект C ++ 2012-11-02 не дает обоснования этого правила, а лишь несколько примеров.

8.4.3 Удаленные определения [dcl.fct.def.delete]
...
3 [ Пример : можно принудительно выполнить инициализацию не по умолчанию и нецелочисленную инициализацию с помощью

struct onlydouble {  
  onlydouble() = delete; // OK, but redundant  
  onlydouble(std::intmax_t) = delete;  
  onlydouble(double);  
};  

- конец примера ]
[ Пример : можно предотвратить использование класса в некоторых новых выражениях, используя удаленные определения объявленного пользователем оператора new для этого класса.

struct sometype {  
  void *operator new(std::size_t) = delete;  
  void *operator new[](std::size_t) = delete;  
};  
sometype *p = new sometype; // error, deleted class operator new  
sometype *q = new sometype[3]; // error, deleted class operator new[]  

- конец примера ]
[ Пример : можно сделать класс не копируемым, то есть доступным только для перемещения, используя удаленные определения конструктора копирования и оператора присваивания копии, а затем предоставив определения по умолчанию для конструктора перемещения и оператора присваивания перемещения.

struct moveonly {  
  moveonly() = default;  
  moveonly(const moveonly&) = delete;  
  moveonly(moveonly&&) = default;  
  moveonly& operator=(const moveonly&) = delete;  
  moveonly& operator=(moveonly&&) = default;  
  ~moveonly() = default;  
};  
moveonly *p;  
moveonly q(*p); // error, deleted copy constructor  

- конец примера ]

Олаф Дитше
источник
4
Обоснование кажется очень ясным из примеров, не так ли?
Джесси Гуд