在 C + + 中通过引用传递参数时的默认值

当我们通过引用传递参数时,是否可以为函数的参数提供默认值。在 C + + 中

例如,当我试图声明如下函数时:

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

当我这样做时,它会出现一个错误:

错误 C2440: “默认参数”: 无法从“ const int”转换为“ unsigned long &” 不是“ const”的引用不能绑定到非 lvalue

156944 次浏览

可以对常量引用执行此操作,但不能对非常量引用执行此操作。这是因为 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
}

只有一种情况真正需要使用默认值,那就是在构造函数上。不可能从另一个构造函数调用一个构造函数,因此这种技术在这种情况下不起作用。

不能对默认参数使用常量文本的原因与不能将常量文本用作函数调用的参数的原因相同。引用值必须有一个地址,常量引用值不需要(即它们可以是 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 的参数。通常的方法是让用户在这种情况下传递一个指针。如果他们不想让你填入值,他们会传入一个 NULL 指针。我喜欢空对象方法。它使调用者的生活变得更简单,而不会使被调用者的代码变得非常复杂。

通过引用传递参数有两个原因: (1)为了性能(在这种情况下,您希望通过常量引用传递参数)和(2)因为您需要能够更改函数内参数的值。

我非常怀疑,在现代架构上传递一个未签名的长句会让您的速度变慢太多。所以我假设您打算改变方法中 State的值。编译器正在抱怨,因为常量 0不能更改,因为它是 rvalue (错误消息中的“ non-lvalue”)和 unchangable (错误消息中的 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);

当然,我不是很确定你在写什么或者写到哪里,但这是另一个问题了。

仍然存在提供可选参数的老式 C 方法: 当不存在时,指针可以为 NULL:

void write( int *optional = 0 ) {
if (optional) *optional = 5;
}
void revealSelection(const ScrollAlignment& = ScrollAlignment::alignCenterIfNeeded, bool revealExtent = false);

这个小模板会帮助你:

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);

另一种方式可能是:

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


// wrapper
const ULONG Write(bool sequence = true)
{
ULONG dummy;
return Write(dummy, sequence);
}

那么下列呼叫是可能的:

ULONG State;
object->Write(State, false); // sequence is false, "returns" State
object->Write(State); // assumes sequence = true, "returns" State
object->Write(false); // sequence is false, no "return"
object->Write(); // assumes sequence = true, no "return"

虚拟常量 ULONG 写(ULONG & State = 0,bool 序列 = true) ;

答案很简单,我不太擅长解释,但是如果你想把一个默认值传递给一个非常量参数,这个参数可能会在这个函数中被修改,你可以这样使用它:

virtual const ULONG Write(ULONG &State = *(ULONG*)0, bool sequence =
> true);
void f(const double& v = *(double*) NULL)
{
if (&v == NULL)
cout << "default" << endl;
else
cout << "other " << v << endl;
}

在 OO 的情况下... 说一个给定的类具有和“ Default”意味着这个 Default (值)必须相应地声明一个 then 可以用作 Default Parameter ex:

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() ) {

这种解决方案非常合适,因为在这种情况下,“全局默认分页”是一个单一的“引用”值。您还可以在运行时更改默认值,比如“全局级”配置,例如: 用户分页导航首选项等等。.

可以用 const 修饰语表示 State:

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

这里还有一个相当肮脏的伎俩:

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

在这种情况下,你必须用 std::move调用它:

ULONG val = 0;
Write(std::move(val));

这只是一些有趣的变通方法,我 完全不推荐在实际代码中使用它!

我有一个解决这个问题的方法,请参阅下面关于 int&默认值的示例:

class Helper
{
public:
int x;
operator int&() { return x; }
};


// How to use it:
void foo(int &x = Helper())
{


}

您可以对任何您想要的琐碎数据类型执行此操作,例如 booldouble..。

定义2个重载函数。

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


virtual const ULONG Write(bool sequence = true)
{
int State = 0;
return Write(State, sequence);
}