void - причудливая бородавка в системе типов C++. Это неполный тип, который не может быть завершен, и у него есть все виды магических правил об ограниченных способах его использования:
Тип cv
void- это неполный тип, который не может быть завершен; такой тип имеет пустой набор значений. Он используется в качестве типа возврата для функций, которые не возвращают значение. Любое выражение может быть явно преобразовано в тип cvvoid([expr.cast]). Выражение типа сортаvoidдолжен быть использован только как выражение выражение, в качестве операнда выражения запятой, в качестве второго или третьего операнда?:([expr.cond]), как операндtypeid,noexceptилиdecltype, как выражение в оператореreturnдля функции с возвращаемым типом cvvoid, или как операнд явного преобразования в тип cvvoid.
(N4778, [basic.fundamental] №9)
Помимо зудящего чувства ко всем этим странным правилам, из-за ограниченных способов его использования, он часто выступает в качестве болезненного особого случая при написании шаблонов; чаще всего кажется, что мы хотели бы, чтобы он вел себя больше как std::monostate.
Давайте на минуту представим, что вместо приведенной выше цитаты стандарт говорит о void что-то вроде
Это тип с определением, эквивалентным:
struct void { void()=default; template<typename T> explicit void(T &&) {}; // to allow cast to void };
сохраняя магию void * - может иметь псевдоним любого объекта, указатели данных должны выжить при прохождении через void *.
Это:
- должен охватывать существующие случаи использования
voidтипа "правильные"; - Возможно, можно было бы разрешить удаление приличного количества мусора, распространяемого по стандарту - например, [expr.cond] ¶2, вероятно, будет ненужным, а [stmt.return] будет значительно упрощен (при сохранении "исключения", которое
returnбез выражения допустим дляvoidи то, что функция "вытекает" из функцииvoidэквивалентнаreturn;); - должен быть таким же эффективным - оптимизация пустых классов в настоящее время поддерживается повсеместно;
- быть по сути совместимым с современными ABI, и может быть еще компилятором специально для более старых.
Помимо совместимости, это обеспечит:
- конструирование, копирование и перемещение этих пустых объектов, исключая особые случаи, обычно необходимые в шаблонах;
- арифметика бонусного указателя на
void *, действующая как дляchar *, который является распространенным расширением, весьма полезен при манипулировании двоичными буферами.
Теперь, помимо возможно измененных возвращаемых значений вещи <type_traits>, что это может нарушить в коде, который правильно сформирован в соответствии с текущими (C++ 17) правилами?