Почему void не принимает значение void в C ++? - PullRequest
5 голосов
/ 02 апреля 2012

Мне любопытно, почему C ++ не определяет void через:

typedef struct { } void;

т.е. каково значение в типе, который не может быть создан, даже если в результате этой установки код не создается?

Если мы используем gcc -O3 -S, то оба следующих продукта производят идентичный ассемблер:

int main() { return 0; }

и

template <class T> T f(T a) { }
typedef struct { } moo;
int main() { moo a; f(a); return 0; }

Это имеет смысл. A struct { } просто принимает пустое значение, достаточно простое для оптимизации. На самом деле странная часть заключается в том, что они производят другой код без -O3.

Однако вы не можете использовать этот же трюк просто с помощью typedef void moo, потому что void не может принимать ни одно значение, даже пустое. Есть ли у этого различия какая-либо полезность?

Существуют различные другие строго типизированные языки, такие как Haskell, и, по-видимому, ML, которые имеют значение для своего типа void, но не предлагают явно ничего не значащие типы, хотя некоторые обладают собственными типами указателей, которые действуют как void *.

Ответы [ 3 ]

4 голосов
/ 02 апреля 2012

Я вижу обоснование для void невозможности создания экземпляра, происходящего из C-корней C ++. В старые кровавые времена безопасность типов не была такой уж большой проблемой, и void* постоянно проходили. Тем не менее, вы всегда можете посмотреть на код, который буквально не говорит void* (из-за typedef s, макросы и C ++, шаблоны), но все же означает, что он

Немного безопасности, если вы не можете разыменовать void*, но сначала должны привести его к указателю на правильный тип. Добьетесь ли вы этого с помощью неполного типа, как подсказывает Бен Фойгт , или если вы используете встроенный тип void, не имеет значения. Вы защищены от неправильного предположения , что вы имеете дело с типом, которым вы не являетесь.

Да, void вводит раздражающие особые случаи, особенно при разработке шаблонов. Но хорошо (то есть, намеренно), что компилятор не принимает попытки создания экземпляров void.

.
3 голосов
/ 02 апреля 2012

Почему должен void быть неполным типом?

Есть много причин.

Самое простое это: moo FuncName(...) все равно должен что-то возвращать. Является ли это нейтральным значением, является ли это «значением не значения», или чем-то еще; он все равно должен сказать return value;, где value - это какое-то действительное значение. Надо сказать return moo();.

Зачем писать функцию, которая технически возвращает что-то, если она на самом деле не возвращает что-то? Компилятор не может оптимизировать возвращаемый результат, потому что он возвращает значение полного типа. Абонент может использовать его.

C ++ не все шаблоны, вы знаете. Компилятор не обязательно обладает знаниями, чтобы выбросить все. Часто приходится выполнять оптимизацию в функции, в которой нет сведений о внешнем использовании этого кода.

void в данном случае означает «Я ничего не возвращаю». Существует фундаментальная разница между этим и «я возвращаю бессмысленную ценность». Это допустимо:

moo FuncName(...) { return moo(); }

moo x = FuncName(...);

Это в лучшем случае вводит в заблуждение. Беглый просмотр предполагает, что значение возвращается и сохраняется. Идентификатор x теперь имеет значение и может использоваться для чего-либо.

Тогда как это:

void FuncName(...) {}

void x = FuncName(...);

- ошибка компилятора. Так это:

void FuncName(...) {}

moo x = FuncName(...);

Понятно, что происходит: FuncName не имеет возвращаемого значения.

Даже если вы разрабатываете C ++ с нуля, без задержек от C, вам все равно понадобится какое-то ключевое слово, чтобы указать разницу между функцией, которая возвращает значение, и функцией, которая не возвращает.

Кроме того, void* является особенным, потому что void не является полным типом. Поскольку стандарт требует, чтобы он не был полным типом, концепция void* может означать «указатель на нетипизированную память». Это специализированная семантика литья. Указатели на типизированную память могут быть неявно приведены к нетипизированной памяти. Указатели на нетипизированную память можно явно привести к типизированному указателю, как это было раньше.

Если вместо этого использовать moo*, то семантика становится странной. У вас есть указатель на напечатанную память. В настоящее время C ++ определяет приведение между несвязанными типизированными указателями (за исключением определенных особых случаев) как неопределенное поведение. Теперь стандарт должен добавить еще одно исключение для типа moo. Он должен сказать, что происходит, когда вы делаете это:

moo *m = new moo;
*moo;

С void это ошибка компилятора. Что это с moo? Это все еще бесполезно и бессмысленно, но компилятор должен это разрешить.

Если честно, я бы сказал, что лучшим вопросом будет «Почему void должен быть завершенный тип?»

3 голосов
/ 02 апреля 2012

Потому что это не сделало бы void неполным типом, не так ли?

Попробуйте еще раз пример кода с:

struct x; // incomplete
typedef x moo;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...