Семантика передачи по ссылке по умолчанию в C++

Обновлено: этот вопрос больше касается языковой инженерии, чем самого C++. Я использовал C++ в качестве примера, чтобы показать, что я хочу, в основном потому, что использую его ежедневно. Я не хотел знать, как это работает на C++, но начал обсуждение того, как это сделать мог.

Это не то, как это работает сейчас, это то, как я желание это могло бы сделать, и это наверняка нарушило бы совместимость с C, но я думаю, что это именно то, о чем extern "C".

Я имею в виду, что в каждой функции или методе, которые вы объявляете прямо сейчас, вы должны явно указать, что объект будет отправлен по ссылке с префиксом оператора ссылки на нем. Я хочу, чтобы каждый тип, не относящийся к POD, автоматически отправлялся по ссылке, потому что я использую это много, фактически для каждого объекта, размер которого превышает 32 бита, и это почти каждый мой класс.

Давайте проиллюстрируем, как это происходит сейчас, предположим, что а, б и c являются классами:

class example {
    public:
        int just_use_a(const a &object);
        int use_and_mess_with_b(b &object);
        void do_nothing_on_c(c object);
};

Теперь то, что я желаю:

class example {
    public:
        int just_use_a(const a object);
        int use_and_mess_with_b(b object);
        extern "C" void do_nothing_on_c(c object);
};

Теперь do_nothing_on_c () может вести себя так же, как сегодня.

Это было бы интересно, по крайней мере, для меня, кажется намного более ясным, а также, если вы знать, каждый параметр, не относящийся к POD, приходит по ссылке, я считаю, что ошибки будут такими же, как если бы вам пришлось явно объявить его.

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

Что ж, мне потребовалось время, чтобы снова понять вопрос (я использовал diff). Предполагается, что "extern" C "сообщает" передать его по копии, а не по ссылке, как для двух функций выше ".

paercebal 29.09.2008 23:12

Точно. Этот вопрос больше касается языковой инженерии, чем C++. Прошу прощения, если я использую C++ в качестве примера, но это язык, который я использую чаще всего, поэтому, если бы я хотел сделать один C++, был бы моей базой.

Edwin Jarvis 30.09.2008 04:25
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
2
3 312
8

Ответы 8

Я искренне думаю, что вся эта идея передачи по значению / передаче по ссылке в C++ вводит в заблуждение. Все передается по значению. У вас есть три случая:

  1. Где вы передаете локальную копию переменной

    void myFunct(int cantChangeMyValue)
    
  2. Где вы передаете локальную копию указателя на переменную

    void myFunct(int* cantChangeMyAddress) {
        *cantChangeMyAddress = 10;
    }
    
  3. Если вы передаете «ссылку», но с помощью магии компилятора это так же, как если бы вы передавали указатель и просто разыменовывали его каждый раз.

    void myFunct(int & hereBeMagic) {
        hereBeMagic = 10; // same as 2, without the dereference
    }
    

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

То, что вы предлагаете, не позволит программисту сделать номер 1. Я лично считаю, что было бы плохой идеей отказаться от этой возможности. Один из основных плюсов C / C++ - мелкозернистое управление памятью. Передача всего по ссылке - это просто попытка сделать C++ более похожим на Java.

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

QBziZ 28.09.2008 22:50

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

Platinum Azure 01.11.2011 18:55

Да, я считаю, что это довольно сбивающая с толку перегрузка.

Вот что Microsoft говорит о ситуации:

Do not confuse reference declarations with use of the address-of operator. When & identifier is preceded by a type, such as int or char, then identifier is declared as a reference to the type. When & identifier is not preceded by a type, the usage is that of the address-of operator.

Я не очень хорошо разбираюсь в C или C++, но у меня возникают большие головные боли, разбирая различные варианты использования * и & на обоих языках, чем я кодирую на ассемблере.

На самом деле это не отличается от использования * в объявлении и * в операторе. Вы можете справиться с этим в C. C++ более ортогонален, поскольку вы можете использовать как &, так и * в объявлении и в качестве оператора.

MSalters 29.09.2008 16:21

Вариант компилятора, который полностью меняет значение части кода, мне кажется плохой идеей. Либо воспользуйтесь синтаксисом C++, либо найдите другой язык.

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

Каждая перестановка (по значению / по ссылке и константная / неконстантная) имеет важные различия, которые определенно не эквивалентны.

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

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

Кодовая база, полностью верная по константам, идет еще дальше, добавляя константу в конец прототипов. Рассматривать:

void Foo::PrintStats( void ) const {
   /* Cannot modify Foo member variables */
}

void Foo::ChangeStats( void ) {
   /* Can modify foo member variables */
}

Если вам нужно передать объект Foo в функцию с префиксом const, вы можете вызвать PrintStats (). Компилятор выдает ошибку при вызове ChangeStats ().

void ManipulateFoo( const Foo &foo )
{
    foo.PrintStats();  // Works
    foo.ChangeStats(); // Oops; compile error
}

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

Основная причина, по которой ссылки были добавлены в C++, - это для поддержки перегрузки оператора; если вам нужна семантика «передачи по ссылке», у C был вполне разумный способ сделать это: указатели.

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

Также см

I do want to change the argument, should I use a pointer or should I use a reference? I don't know a strong logical reason. If passing ``not an object'' (e.g. a null pointer) is acceptable, using a pointer makes sense. My personal style is to use a pointer when I want to modify an object because in some contexts that makes it easier to spot that a modification is possible.

из того же FAQ.

есть что-то непонятное. когда ты сказал:

int b(b &param);

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

class B{/*something...*/};
int b(B& param);

С этого момента, я полагаю, вы имеете в виду то, что я пишу.

Теперь ваш вопрос: «Не думаете ли вы, что будет лучше, если компилятор будет рассматривать каждую передачу по значению не-POD как передачу по ссылке?». Первая проблема в том, что это нарушит ваш контракт. Я полагаю, вы имеете в виду передачу по CONST-ссылке, а не только по ссылке.

Теперь ваш вопрос сводится к следующему: «знаете ли вы, есть ли какая-нибудь директива компилятора, которая может оптимизировать вызов функции по значению?»

Теперь ответ - «не знаю».

b - это тип, я просто не сказал, я подумал, будет понятно, функция имеет то же имя только потому, что она возится с b, это может быть do_something_nice_with_b (b & object).

Edwin Jarvis 29.09.2008 03:36

Я думаю, что C++ станет очень запутанным, если вы начнете смешивать все виды доступных параметров с их константными вариациями.

Отслеживание всех вызовов конструкторов копирования, всех перегруженных разыменований и т. д. Быстро выходит из-под контроля.

Вы поняли, что нужно упростить синтаксис и использовать одну конкретную семантику. Эти предлагаемые изменения - способ преодолеть этот беспорядок с множеством возможностей. Я думаю, что одним из главных критиков C++ является то, что он пытается сделать слишком много вещей.

Edwin Jarvis 30.09.2008 04:38

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