Подтвердить что ты не робот

Почему невозможно иметь ссылку на пустоту?

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

Декларатор, который указывает тип "ссылка на cv void", плохо сформирован.

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

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


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

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
}
4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

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

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

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

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

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

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

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

logger << data;

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

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

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

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

Вы не можете сделать НИЧЕГО для данных EXCEPT, чтобы получить адрес, и в нем есть отличный и безопасный метод, т.е.

void* data;

Разве это имеет смысл?

Ответ 3

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

Ответ 4

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

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


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

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

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

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

2 Вы ничего не смогли бы с этим сделать

При использовании указателя 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!

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

Ответ 5

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

Ответ 6

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

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

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

Согласен ли кто-нибудь с этим?

Ответ 7

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

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

Ответ 8

Если бы они были, они были бы семантически недифференцированы от указателей и составляли бы синтаксический сахар. В ссылке говорится: "Я имею в виду то, что относится к этому типу". Разрешение пустой или нулевой ссылки ослабит это отличие от указателей.

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

Ответ 9

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

Моя компания была одной из первых, использующих С++ на коммерческой основе и первоначально скомпилированной с использованием 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, а скорее типизированная ссылка с потенциально недействительной привязкой! Один момент подумал, вероятно, должен предложить лучшие альтернативы этой конкретной идиоме...

Ответ 10

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