Почему С++ 11 делает функции "delete
d" участвовать в разрешении перегрузки?
Почему это полезно? Или, другими словами, почему они скрыты, а не полностью удаляются?
Почему С++ 11-удаленные функции участвуют в разрешении перегрузки?
Ответ 1
Половина цели синтаксиса = delete
заключается в том, чтобы запретить людям вызывать определенные функции с определенными параметрами. Это главным образом для предотвращения неявных преобразований в определенных конкретных сценариях. Чтобы запретить конкретную перегрузку, она должна участвовать в разрешении перегрузки.
Ответ, который вы цитируете, дает вам прекрасный пример:
struct onlydouble {
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
Если delete
полностью удалило эту функцию, это сделало бы синтаксис = delete
эквивалентным этому:
struct onlydouble2 {
onlydouble2(double);
};
Вы можете сделать это:
onlydouble2 val(20);
Это законный С++. Компилятор будет смотреть на все конструкторы; ни один из них не принимает целочисленный тип напрямую. Но один из них может принять его после неявного преобразования. Так оно и будет называть.
onlydouble val(20);
Это не законный С++. Компилятор рассмотрит все конструкторы, включая delete
d. Он увидит точное соответствие через std::intmax_t
(который будет точно соответствовать любому целочисленному литералу). Таким образом, компилятор выберет его, а затем немедленно выдаст ошибку, потому что он выбрал функцию delete
d.
= delete
означает "я запрещаю это" , а не просто: "Этого не существует". Это гораздо более сильное утверждение.
Я спрашивал, почему стандарт С++ говорит = delete означает "я запрещаю это" вместо "этого не существует"
Это потому, что нам не нужна специальная грамматика, чтобы сказать "этого не существует". Мы получаем это неявно, просто не объявляя конкретный "this". "Я запрещаю это" представляет собой конструкцию, которая не может быть достигнута без специальной грамматики. Поэтому мы получаем специальную грамматику, чтобы сказать "я запрещаю это" , а не другую.
Единственная функциональность, которую вы могли бы получить, имея явную "эту не существующую" грамматику, заключалась бы в том, чтобы не допустить, чтобы кто-то позже объявил, что она существует. И это просто недостаточно полезно, чтобы иметь собственную грамматику.
иначе нет способа объявить, что конструктор копирования не существует, и его существование может вызвать бессмысленные неоднозначности.
Конструктор копирования является специальной функцией-членом. У каждого класса всегда есть конструктор копирования. Так же, как они всегда имеют оператор присваивания копий, перемещают конструктор и т.д.
Эти функции существуют; вопрос только в том, законно ли их называть. Если вы попытались сказать, что = delete
означает, что они не существуют, тогда спецификация должна будет объяснить, что означает, что функция не существует. Это не понятие, которое обрабатывает спецификация.
Если вы попытаетесь вызвать функцию, которая еще не была объявлена /определена, компилятор будет ошибочен. Но это будет ошибкой из-за идентификатора undefined, а не из-за ошибки "функция не существует" (даже если ваш компилятор сообщает об этом именно так). Различные конструкторы все называются разрешением перегрузки, поэтому их "существование" обрабатывается в этом отношении.
В каждом случае есть либо функция, объявленная через идентификатор, либо конструктор/деструктор (также объявленный через идентификатор, только идентификатор типа). Перегрузка оператора скрывает идентификатор синтаксического сахара, но он все еще там.
Спецификация С++ не может обрабатывать концепцию "функции, которая не существует". Он может обрабатывать несоответствие перегрузки. Он может обрабатывать неоднозначность перегрузки. Но он не знает о том, чего там нет. Таким образом, = delete
определяется в терминах гораздо более полезных "попыток вызвать этот отказ", а не менее полезного "притворяться, что я никогда не писал эту строку".
И снова перечитайте первую часть. Вы не можете этого сделать, поскольку функция не существует. Это еще одна причина, почему он определил этот путь: поскольку один из основных вариантов использования синтаксиса = delete
заключается в том, чтобы заставить пользователя использовать определенные типы параметров, явно указывать и т.д. В принципе, для предотвращения неявных преобразований типов.
Ваше предложение не сделает этого.
Ответ 2
Рабочий проект С++ 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);
};
- конечный пример]
[Пример. Можно запретить использование класса в некоторых новых выражениях с помощью удаленных определений объявленный пользователем оператор для этого класса.
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
- конец примера]