void
- причудливая бородавка в системе типов C ++. Это неполный тип, который не может быть завершен, и у него есть все виды магических правил об ограниченных способах его использования:
Тип cv void
- это неполный тип, который не может быть завершен; такой тип имеет пустой набор значений. Он используется в качестве типа возврата для функций, которые не возвращают значение.
Любое выражение может быть явно преобразовано в тип cv void
([expr.cast]).
Выражение типа cv void
должно использоваться только как выражение выражения, как операнд выражения запятой, как второй или третий операнд ?:
([expr.cond]), так как операнд typeid
, noexcept
или decltype
, как выражение в операторе return
для функции с типом возврата cv void
, или как операнд явного преобразование в тип cv void
.
(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) правилами?