Чому C ++ 11 змушує функції " delete
d" брати участь у вирішенні перевантажень ?
Чому це корисно? Або іншими словами, чому вони приховані, а не повністю видалені?
Чому C ++ 11 змушує функції " delete
d" брати участь у вирішенні перевантажень ?
Чому це корисно? Або іншими словами, чому вони приховані, а не повністю видалені?
Відповіді:
Половина цілі = delete
синтаксису полягає в тому, щоб перешкоджати людям викликати певні функції з певними параметрами. Це головним чином для запобігання неявних перетворень у певних конкретних сценаріях. Щоб заборонити певне перевантаження, він повинен брати участь у вирішенні перевантаження.
Відповідь, яку ви цитуєте, дає вам прекрасний приклад:
struct onlydouble {
onlydouble(std::intmax_t) = delete;
onlydouble(double);
};
Якщо повністю delete
вилучити функцію, це зробить = delete
синтаксис таким:
struct onlydouble2 {
onlydouble2(double);
};
Ви можете зробити це:
onlydouble2 val(20);
Це легальний C ++. Компілятор розгляне всі конструктори; жоден з них не приймає цілочисельний тип безпосередньо. Але один з них може прийняти це після неявного перетворення. Тож це буде називати це.
onlydouble val(20);
Це не є законним C ++. Компілятор розгляне всі конструктори, включаючи delete
d. Він побачить точну відповідність через std::intmax_t
(яка точно відповідатиме будь-якому цілочисельному літералу). Тож компілятор виділить його, а потім негайно видасть помилку, оскільки вибрав функцію delete
d.
= delete
означає "Я забороняю це", а не просто "Це не існує". Це набагато сильніше твердження.
Я запитував, чому стандарт C ++ говорить = delete означає "я забороняю це" замість "це не існує"
Це тому, що нам не потрібна спеціальна граматика, щоб сказати "цього не існує". Ми отримуємо це неявно, просто не оголошуючи конкретне "це", про яке йдеться. "Я забороняю це" являє собою конструкцію, якої неможливо досягти без спеціальної граматики. Тож ми отримуємо спеціальну граматику, щоб сказати "я забороняю це", а не інше.
Єдиною функціональністю, яку ви отримаєте, маючи явну граматику "цього не існує", було б запобігти тому, щоб хтось згодом оголосив, що вона існує. І це просто недостатньо корисно, щоб мати потребу в власній граматиці.
інакше не можна заявити, що конструктор копій не існує, і його існування може спричинити безглузді двозначності.
Конструктор копіювання - це спеціальна функція-член. Кожен клас завжди має конструктор копіювання. Так само, як у них завжди є оператор присвоєння копії, конструктор переміщення тощо.
Ці функції існують; питання полягає лише в тому, чи законно їх називати. Якщо ви спробували сказати, що це = delete
означало, що вони не існують, тоді специфікація повинна була б пояснити, що означає, що функція не існує. Це не концепція, яку обробляє специфікація.
Якщо ви спробуєте викликати функцію, яка ще не оголошена / визначена, то компілятор помилиться. Але це буде помилка через невизначений ідентифікатор , а не через помилку "функція не існує" (навіть якщо ваш компілятор повідомляє про це таким чином). Всі різні конструктори називаються дозволом перевантаження, тому в цьому відношенні обробляється їхнє "існування".
У кожному випадку існує або функція, оголошена за допомогою ідентифікатора, або конструктор / деструктор (також оголошена за допомогою ідентифікатора, просто ідентифікатор типу). Перевантаження оператора приховує ідентифікатор за синтаксичним цукром, але він все ще є.
Специфікація С ++ не може обробляти концепцію "функції, яка не існує". Він може усунути невідповідність перевантаження. Це може впоратися з неоднозначністю перевантаження. Але воно не знає про те, чого там немає. Це = delete
визначається з точки зору набагато корисніших "спроб назвати це невдалою", а не менш корисних "вдавати, що я ніколи не писав цього рядка".
І знову перечитайте першу частину. Ви не можете цього зробити за допомогою "функція не існує". Це ще одна причина, чому його визначають таким чином: оскільки одним із основних випадків використання = delete
синтаксису є можливість змусити користувача використовувати певні типи параметрів, явно здійснювати приведення тощо. В основному, щоб запобігти неявним перетворенням типу.
Ваша пропозиція цього не зробить.
= delete
на увазі "цей член не існує", що означатиме, що він не може брати участь у вирішенні перевантажень.
= delete
мається на увазі "цей член не існує", то перший приклад, який я опублікував, не зможе перешкодити людям передавати цілі числа onlydouble
конструктору ' , оскільки onlydouble
видалене перевантаження не існувало б . Він не братиме участі у вирішенні перевантажень, а отже, не завадить передавати цілі числа. Що становить половину точки = delete
синтаксису: щоб мати можливість сказати: "Ви не можете неявно передати X цій функції".
=delete
? Зрештою, ми можемо сказати "не підлягає копіюванню", виконавши те саме те саме: оголосивши конструктор / призначення копіювання приватним. Також зауважте, що оголошення чогось приватного не робить його неможливим; код всередині класу все ще може його викликати. Тож це не те саме, що = delete
. Ні, = delete
синтаксис дозволяє нам робити щось дуже незручне та незрозуміле раніше набагато більш очевидним та розумним способом.
Робочий проект C ++ 02.11.2012 не дає обгрунтування цього правила, лише деякі приклади
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
- кінцевий приклад ]