Глобалы и / или синглтоны не являются злыми по своей природе.
Я пришел из системного администратора, оболочки, Perl (и моего «реального» программирования), фона типа PHP; В прошлом году меня бросили на концерт Java.
Синглтоны - это зло. Глобалы настолько злы, что их даже не пускают. Тем не менее, в Java есть такие вещи, как AOP, и теперь различные фреймворки «Dependency Injection» (мы использовали Google Guice). АОП меньше так, но DI вещи наверняка дадут вам что? Глобал. Ух, спасибо.
Вы не можете использовать константный литерал для параметра по умолчанию по той же причине, по которой вы не можете использовать его в качестве параметра для вызова функции. Справочные значения должны иметь адрес, постоянные ссылочные значения не должны (т.е. они могут быть 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-указатель, если не хотят, чтобы вы вводили значение. Мне нравится подход с нулевым объектом. Это облегчает жизнь вызывающим абонентам, не сильно усложняя код вызываемого абонента.
Вы можете сделать это для константной ссылки, но не для неконстантной. Это связано с тем, что C ++ не позволяет привязывать временную (в данном случае значение по умолчанию) к неконстантной ссылке.
Одним из способов решения этой проблемы было бы использование фактического экземпляра по умолчанию:
static int AVAL = 1;
void f( int & x = AVAL ) {
// stuff
}
int main() {
f(); // equivalent to f(AVAL);
}
, но это очень ограниченного практического использования.
Нет, это невозможно.
Передача по ссылке подразумевает, что функция может изменить значение параметра. Если параметр не предоставляется вызывающей стороной и исходит из константы по умолчанию, какую функцию предполагается изменить?
Об этом уже говорилось в одном из прямых комментариев к вашему ответу, но просто чтобы заявить об этом официально. Вы хотите использовать перегрузку:
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
}
Есть только одна ситуация, когда действительно необходимо использовать значение по умолчанию, и это находится на конструкторе. Невозможно вызвать один конструктор из другого, поэтому в этом случае этот метод не работает.
Я думаю, что нет, и причина в том, что значения по умолчанию оцениваются как константы, а значения, передаваемые по ссылке, должны иметь возможность изменяться, если вы также не объявите, что это постоянная ссылка.
Есть две причины для передачи аргумента по ссылке: (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);
Конечно, я не совсем уверен, что вы пишете и куда. Но это'
Там еще есть старый поставку предоставления дополнительных аргументов: указатель, который может быть нулевым, когда нет:
void write( int *optional = 0 ) {
if (optional) *optional = 5;
}