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) правилами?