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

Глобалы и / или синглтоны не являются злыми по своей природе.

Я пришел из системного администратора, оболочки, Perl (и моего «реального» программирования), фона типа PHP; В прошлом году меня бросили на концерт Java.

Синглтоны - это зло. Глобалы настолько злы, что их даже не пускают. Тем не менее, в Java есть такие вещи, как AOP, и теперь различные фреймворки «Dependency Injection» (мы использовали Google Guice). АОП меньше так, но DI вещи наверняка дадут вам что? Глобал. Ух, спасибо.

113
задан Drew Noakes 12 May 2012 в 19:19
поделиться

7 ответов

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

int* foo (int& i )
{
   return &i;
}

foo(0); // compiler error.

const int* bar ( const int& i )
{
   return &i;
}

bar(0); // ok.

Убедитесь, что у вашего значения по умолчанию есть адрес, и все в порядке.

int null_object = 0;

int Write(int &state = null_object, bool sequence = true)
{
   if( &state == &null_object )
   {
      // called with default paramter
      return sequence? 1: rand();
   }
   else
   {
      // called with user parameter
      state += sequence? 1: rand();
      return state;
   }
}

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

5
ответ дан 24 November 2019 в 02:43
поделиться

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

Одним из способов решения этой проблемы было бы использование фактического экземпляра по умолчанию:

static int AVAL = 1;

void f( int & x = AVAL ) {
   // stuff
} 

int main() {
     f();       // equivalent to f(AVAL);
}

, но это очень ограниченного практического использования.

95
ответ дан 24 November 2019 в 02:43
поделиться

Нет, это невозможно.

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

5
ответ дан 24 November 2019 в 02:43
поделиться

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

virtual const ULONG Write(ULONG &State, bool sequence);
inline const ULONG Write()
{
  ULONG state;
  bool sequence = true;
  Write (state, sequence);
}

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

class A {}; 
class B {}; 
class C {};

void foo (A const &, B const &, C const &);
void foo (B const &, C const &); // A defaulted
void foo (A const &, C const &); // B defaulted
void foo (C const &); // A & B defaulted etc...

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

class Base {
public:
  virtual void f1 (int i = 0);  // default '0'

  virtual void f2 (int);
  inline void f2 () {
    f2(0);                      // equivalent to default of '0'
  }
};

class Derived : public Base{
public:
  virtual void f1 (int i = 10);  // default '10'

  using Base::f2;
  virtual void f2 (int);
};

void bar ()
{
  Derived d;
  Base & b (d);
  d.f1 ();   // '10' used
  b.f1 ();   // '0' used

  d.f2 ();   // f1(int) called with '0' 
  b.f2 ();   // f1(int) called with '0
}

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

29
ответ дан 24 November 2019 в 02:43
поделиться

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

1
ответ дан 24 November 2019 в 02:43
поделиться

Есть две причины для передачи аргумента по ссылке: (1) для производительности (в этом случае вы хотите передать по константной ссылке) и (2) потому что вам нужна возможность изменить значение аргумента внутри функции.

Я очень сомневаюсь, что передача unsigned long на современных архитектурах слишком сильно замедляет вас. Итак, я предполагаю, что вы собираетесь изменить значение State внутри метода. Компилятор жалуется, потому что константа 0 не может быть изменена, так как это rvalue ("не-lvalue" в сообщении об ошибке) и неизменяемое ( const в сообщении об ошибке).

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

Другими словами, Не- const ссылки должны ссылаться на фактические переменные. Значение по умолчанию в сигнатуре функции ( 0 ) не является реальной переменной. Вы столкнулись с той же проблемой, что и:

struct Foo {
    virtual ULONG Write(ULONG& State, bool sequence = true);
};

Foo f;
ULONG s = 5;
f.Write(s); // perfectly OK, because s is a real variable
f.Write(0); // compiler error, 0 is not a real variable
            // if the value of 0 were changed in the function,
            // I would have no way to refer to the new value

Если вы на самом деле не собираетесь изменять State внутри метода, вы можете просто изменить его на const ULONG & . Но вы не получите от этого большого выигрыша в производительности, поэтому я бы рекомендовал изменить его на нереференсный ULONG . Я заметил, что вы уже возвращаете ULONG , и у меня есть скрытое подозрение, что его значением является значение State после любых необходимых изменений. В этом случае я бы просто объявил метод так:

// returns value of State
virtual ULONG Write(ULONG State = 0, bool sequence = true);

Конечно, я не совсем уверен, что вы пишете и куда. Но это'

6
ответ дан 24 November 2019 в 02:43
поделиться

Там еще есть старый поставку предоставления дополнительных аргументов: указатель, который может быть нулевым, когда нет:

void write( int *optional = 0 ) {
    if (optional) *optional = 5;
}
23
ответ дан 24 November 2019 в 02:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: