Вопрос

Почему невозможно иметь ссылку на void?Единственное, что я нашел в стандарте C++, это строка: 8.3.2.1

Декларатор, указывающий тип «ссылка на резюме void» имеет неправильную форму.

Почему именно так?Почему я не могу написать «универсальную» функцию, принимающую void&?

Чтобы внести ясность: я не имею в виду полезного приложения, в котором использование ссылки на void могло бы быть лучше, чем использование шаблонов, но мне просто интересно обоснование запрета этой конструкции.


Чтобы немного прояснить: я понимаю, что использование ссылки на void «как есть» было бы так же бессмысленно, как разыменование указателя на void.Однако я мог бы привести его к ссылке на-какой-то тип чтобы использовать его, не так ли?На самом деле, я не понимаю, почему следующий фрагмент может работать...

void foo(void *data)
{
    int *i = reinterpret_cast<int*>(data);
    // do something with i
}

...а этот не может:

void foo(void &data)
{
    int &i = reinterpret_cast<int&>(data);
    // do something with i
}
Это было полезно?

Решение

Если бы у вас была ссылка на void, что бы вы с ней сделали?Это не будет число, символ, указатель или что-то в этом роде.Ваша гипотетическая универсальная функция не может выполнить с ним никаких операций, кроме как получить его адрес (а не его размер).

«пустота» имеет два применения:отказаться от каких-либо знаний о типе (как в void *) и ничего не указывать в отличие от чего-то (возврат функции void).Ни в том, ни в другом случае о пустом чем-то невозможно сказать ничего, кроме того, что оно может иметь адрес.

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

Другие советы

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

void *p = /*something*/ ;
cout << *p << endl;

Вышеприведенный код не имеет смысла, одна из причин, по которой мы имеем пустоту, заключается в том, что мы можем сказать & «Мне нужно выполнить некоторую работу с общим указателем, и я не знаю и не волнуюсь, на что я указываю Quot ;. По определению компилятор не знает, на что указывает пустота *, поэтому он не может разыменовать его. Вы можете - путем приведения - но компилятор не может.

Ссылка на void страдает от той же проблемы; по определению указанные данные не имеют типа, поэтому на них нельзя ссылаться каким-либо значимым образом.

Чтобы сослаться на него, вам - программисту - нужно привести его к другому типу, тогда вы можете иметь типизированную ссылку на него.

Не уверен, объяснил ли я это так хорошо, как хотел.

Рубен, есть мысли?

РЕДАКТИРОВАТЬ. Чтобы ответить на ваши изменения.

Возьмите первую функцию, где вы передаете void * данные. данные - это абсолютно правильный элемент, вы можете с ним вычислять, или, если у вас реализовано какое-либо ведение журнала, вы можете записать его.

logger << data;

и вы получите адресные данные, на которые указывает. Если вы попытаетесь разыменовать данные, компилятор выдаст вам ошибку (в данный момент у вас нет под рукой компилятора C ++, поэтому не уверен в действительной ошибке). например

void* data = /* some assignment */;
logger << *data; // compiler error.

Теперь компилятор не позволит вам разыменовывать void * по любой причине (это не имеет смысла), то же самое относится и к ссылке на void & amp; data, за исключением того, что это ссылка < em> он неявно разыменовывается все время . Компилятор не позволит вам разыменовать void * в одной операции, он не позволит вам разыменовывать его постоянно.

void& data = /* some assignment *.;
logger << data; // means same as logger << *data above

Вы не можете сделать НИЧЕГО с данными КРОМЕ , чтобы взять их адрес, и для этого есть встроенный в язык очень хороший и безопасный метод, т. е.

void* data;

Это имеет больше смысла?

Ссылка - это ссылка на экземпляр чего-либо. Экземпляр чего-либо не может быть типа void. Любой экземпляр чего-либо должен иметь определенный тип (и, возможно, базовые типы).

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

Две основные причины, по которым ссылка на void запрещена


1 Они были бы совершенно бесполезны.

Действительно, если мы оглянемся назад во времена C, у указателей void было две цели:

  • Управление памятью (например.маллок)
  • Универсальность (написание функций, которые могут принимать аргументы любого типа).

Когда появился C++, шаблоны стали лучшим решением для реализации универсальности.Однако по-прежнему должно было быть возможно индивидуальное управление памятью, а взаимодействие между C++ и C было серьезной проблемой, поэтому void* был сохранен.Гипотетическая ссылка на void не поможет с управлением памятью, а универсальность уже рассмотрена, поэтому по сути от нее почти нет никакой пользы (за исключением гарантии ненулевого значения, описанной ниже).

2 Ты ничего не сможешь с этим поделать

При использовании указателя void вы не можете его разыменовать;транспонировано на случай ссылок, это означает, что вы не можете использовать (всегда гипотетическую) ссылку void.Так

void *data = // something
// using *data and data-> is forbidden

void &data = // something
// using data is forbidden

Однако мы могли бы придумать вариант использования, в котором ссылку не нужно было бы «разыменовывать» (эта фраза ужасно неверна, но вы поняли мою точку зрения), а где мы возьмем только ее адрес.Предположим, у меня есть следующая функция:

void foo(void *dataptr)
{
    assert(dataptr != NULL); // or != 0
    // do something with dataptr
}

Чтобы избежать этого раздражающего утверждения, я мог бы написать функцию следующим образом:

void foo(void &dataref)
{
    void *data = &dataref;
    // do something with data
}

Однако, чтобы это работало, &dataref должно быть эквивалентно dataptr, это не так: &dataref эквивалентно &*dataptr!

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

Технически говоря, все, что гарантировано, это то, что ссылка на объект является псевдонимом для него. То, что передача указателей под капотом делается с помощью указателей, является деталью реализации. Это может сбить с толку из-за повторного использования ссылок & Amp; оператор, который также является адресом, но имейте в виду, что оператор фактически имеет разные значения в разных контекстах (в объявлении переменной или параметра он обозначает ссылочный тип, в противном случае это адрес-адрес, кроме случаев, когда он является побитовым-и). Поскольку технически это просто псевдоним для объекта, ссылка всегда разыменовывается, как объяснил Ворриер.

Хорошо, меня беспокоит одна вещь. Идея void*, как упоминалось выше, состоит в том, что у вас все еще есть допустимая переменная, содержащая адрес, но тип игнорируется. Это кажется допустимым, поскольку мы все еще можем работать с адресными данными - тип в этом контексте несколько избыточен (или менее важен). Разыменование это плохо, потому что пытаться получить доступ к члену не имеет смысла, например. p.mem. Мы не знаем, к какому классу обращаться, и, следовательно, к какой памяти нужно переходить, к указателям vtable, которым нужно следовать.

Тем не менее, тогда, похоже, имело бы смысл, что p само по себе будет в порядке, поскольку оно будет ссылаться только на объект, но не на его данные. Для этого не требуется информация о классе, только адрес. Я понимаю, что это абсолютно бесполезно, но важно определить, когда что-то сломалось. Принимая это понятие, ссылка на C ++ (постоянно разыменовывается, но не имеет доступа к чему-либо), например, void& ref = static_cast< &void >(obj) также имеет смысл и, следовательно, допускает недействительные ссылки. Я не говорю, что кто-то должен обсуждать это с ответственными лицами, но из & Понимания смысла & Quot; Точка зрения, это было бы правильно, нет?

Как указывал Люк Торайль (по крайней мере, это моя интерпретация), его можно реализовать, но проблема носит смысловой характер. Разумное объяснение, к которому я мог прийти, заключалось в том, что, поскольку переменная объекта является & Quot; tag & Quot; для последовательности памяти тип имеет важное семантическое значение. Таким образом, указатель, рассматриваемый как переменная со значением адреса, рассматривает тип как несколько лишний, а не ключ к его определению.

Кто-нибудь согласится с этим?

Вы можете рассматривать ссылку как указатель без ссылки. Синтаксически вы обрабатываете ссылку как указатель: вам не нужен оператор * для разыменования, и вы можете использовать его. а не - > чтобы получить доступ к своим членам.

Однако вы не можете разыменовать указатель void. Как указывает Binary Worrier, попытка сделать это приведет к ошибке компиляции. И если у вас не может быть разыменованного пустого указателя, это означает, что у вас не может быть пустого указателя.

Если бы они были, они бы семантически не дифференцировались от указателей и составляли бы синтаксический сахар. Ссылка гласит: & Quot; я имею в виду нечто, относящееся к этому типу. & Quot; Разрешение void или null reference ослабит это отличие от указателей.

Конечно, ссылка может ссылаться на объект, который больше не существует, но это исключение.

Следующее не защищает понятие пустых ссылок. Я предлагаю это как анекдот из дикой природы. Спросите себя, не пахнет ли это смешно.

Моя компания была одной из первых, кто использовал C ++ на коммерческой основе и изначально компилировал с использованием Cfront . Ранние разработчики все еще изучали язык и обычно использовали все приемы в книге (операторы везде!). Вот трюк, который они считали классным:

void Foo::something(int action, ostream &os = *(ostream *)0)
{
   ostream *os_p = &os;
   if (&os == (ostream *)0) {
      os_p = &cerr;
   }
   // continue with method
}

Итак, у вас есть не void-ссылка, а типизированная ссылка с потенциально void связыванием! Подумайте минутку, вероятно, должны предложить лучшие альтернативы этой конкретной идиомы ...

void - это то, чего по определению не существует, поэтому не логично иметь его адрес.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top