Теоретично кажучи, void
це те, що називається іншими мовамиunit
або top
. Його логічний еквівалент - True . Будь-яке значення може бути законно передано void
(кожен тип є підтипом void
). Подумайте про це як про "Всесвіт"; немає жодних операцій, спільних для всіх значень у світі, тому немає дійсних операцій зі значенням типу void
. По-іншому, кажучи вам, що щось належить до набору Всесвіту, ви не має жодної інформації - ви це вже знаєте. Отже, звук наступний:
(void)5;
(void)foo(17); // whatever foo(17) does
Але призначення нижче:
void raise();
void f(int y) {
int x = y!=0 ? 100/y : raise(); // raise() returns void, so what should x be?
cout << x << endl;
}
[[noreturn]]
з іншого боку, називається іноді empty
, Nothing
, Bottom
або Bot
і є логічним еквівалентом False . Він взагалі не має значень, і вираз цього типу може бути передано (тобто є підтипом) будь-якого типу. Це порожній набір. Зауважте, що якщо хтось скаже вам, що "значення виразу foo () належить порожньому набору", воно є дуже інформативним - воно говорить вам про те, що цей вираз ніколи не завершить його нормальне виконання; вона перерве, кине або повісить. Це якраз навпаки void
.
Отже, наступне не має сенсу (псевдо-С ++, оскільки noreturn
не є першокласним типом С ++)
void foo();
(noreturn)5; // obviously a lie; the expression 5 does "return"
(noreturn)foo(); // foo() returns void, and therefore returns
Але завдання нижче є цілком законним, оскільки throw
компілятор розуміє, що він не повертається:
void f(int y) {
int x = y!=0 ? 100/y : throw exception();
cout << x << endl;
}
У ідеальному світі ви можете використати noreturn
як повернене значення для функції, raise()
наведеної вище:
noreturn raise() { throw exception(); }
...
int x = y!=0 ? 100/y : raise();
На жаль, C ++ цього не дозволяє, можливо, з практичних причин. Натомість це дає вам можливість використовувати [[ noreturn ]]
атрибут, який допомагає керувати оптимізаціями та попередженнями компілятора.