Значение по умолчанию для параметра при передаче по ссылке в C ++ - PullRequest
112 голосов
/ 29 июня 2009

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

Например, когда я пытаюсь объявить функцию вроде:

virtual const ULONG Write(ULONG &State = 0, bool sequence = true);

Когда я делаю это, выдает ошибку:

ошибка C2440: «аргумент по умолчанию»: невозможно преобразовать из «const int» в «unsigned long &» Ссылка, которая не относится к const, не может быть связана с ненулевым значением

Ответы [ 16 ]

91 голосов
/ 29 июня 2009

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

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

static int AVAL = 1;

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

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

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

28 голосов
/ 29 июня 2009

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

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
}

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

22 голосов
/ 19 января 2010

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

void write( int *optional = 0 ) {
    if (optional) *optional = 5;
}
11 голосов
/ 29 октября 2011

Этот маленький шаблон поможет вам:

template<typename T> class ByRef {
public:
    ByRef() {
    }

    ByRef(const T value) : mValue(value) {
    }

    operator T&() const {
        return((T&)mValue);
    }

private:
    T mValue;
};

Тогда вы сможете:

virtual const ULONG Write(ULONG &State = ByRef<ULONG>(0), bool sequence = true);
6 голосов
/ 30 июня 2009

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

Я очень сомневаюсь, что пропуск длинного без знака на современных архитектурах слишком сильно тормозит. Поэтому я предполагаю, что вы собираетесь изменить значение 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);

Конечно, я не совсем уверен, что ты пишешь или куда. Но это другой вопрос в другой раз.

5 голосов
/ 30 июня 2009

Нельзя использовать постоянный литерал для параметра по умолчанию по той же причине, по которой вы не можете использовать его в качестве параметра для вызова функции. Ссылочные значения должны иметь адрес, постоянные ссылочные значения не нужны (т. Е. Они могут быть 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-указатель, если не хотят, чтобы вы указали значение. Мне нравится нулевой объектный подход. Это облегчает жизнь звонящим, не усложняя код вызывающего абонента.

4 голосов
/ 29 июня 2009

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

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

1 голос
/ 13 декабря 2014

Это возможно с помощью const квалификатора для State:

virtual const ULONG Write(const ULONG &State = 0, bool sequence = true);
1 голос
/ 14 мая 2014

В случае OO ... Сказать, что данный класс имеет значение «Default», означает, что это значение по умолчанию (значение) должно быть объявлено, а затем может быть использовано в качестве параметра по умолчанию, например:

class Pagination {
public:
    int currentPage;
    //...
    Pagination() {
        currentPage = 1;
        //...
    }
    // your Default Pagination
    static Pagination& Default() {
        static Pagination pag;
        return pag;
    }
};

На вашем методе ...

 shared_ptr<vector<Auditoria> > 
 findByFilter(Auditoria& audit, Pagination& pagination = Pagination::Default() ) {

Это решение вполне подходит, так как в этом случае «Глобальное разбиение на страницы по умолчанию» является единственным «справочным» значением. У вас также будет возможность изменять значения по умолчанию во время выполнения, например, конфигурацию «на уровне глобалов», например: пользовательские настройки навигации по страницам и т. Д.

1 голос
/ 18 октября 2013
void f(const double& v = *(double*) NULL)
{
  if (&v == NULL)
    cout << "default" << endl;
  else
    cout << "other " << v << endl;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...