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

113 c++ pass-by-reference default-value

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

例如,当我尝试声明一个函数时:

virtual const ULONG Write(ULONG &State = 0, bool sequence = true);
Run Code Online (Sandbox Code Playgroud)

当我这样做时,它给出了一个错误:

错误C2440:'default argument':无法从'const int'转换为'unsigned long&'不是'const'的引用不能绑定到非左值

小智 96

您可以为const引用执行此操作,但不能为非const引用执行此操作.这是因为C++不允许将临时(在这种情况下为默认值)绑定到非const引用.

一种方法是使用实​​际实例作为默认值:

static int AVAL = 1;

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

int main() {
     f();       // equivalent to f(AVAL);
}
Run Code Online (Sandbox Code Playgroud)

但这在实际应用中非常有限.

  • boost :: array到rescue void f(int&x = boost :: array <int,1>()[0]){..} :) (3认同)
  • @Sony参考.把它想象成一个地址是害怕的.如果需要地址,请使用指针. (2认同)

Ric*_*den 29

已经在你的答案的一个直接评论中说过,但只是正式说明.你想要使用的是一个过载:

virtual const ULONG Write(ULONG &State, bool sequence);
inline const ULONG Write()
{
  ULONG state;
  bool sequence = true;
  Write (state, sequence);
}
Run Code Online (Sandbox Code Playgroud)

使用函数重载还具有其他好处.首先,您可以默认任何您想要的参数:

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...
Run Code Online (Sandbox Code Playgroud)

也可以在派生类中重新定义虚函数的默认参数,这可以避免重载:

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
}
Run Code Online (Sandbox Code Playgroud)

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

  • 有些人认为默认参数不如超载的巨大乘法那么可怕. (18认同)
  • 在最后一条语句中:从C++ 11开始,您可以使用委托构造函数从另一个构造函数调用一个构造函数以避免代码重复. (4认同)
  • 也许最后,你说:`d.f2(); // f2(int)用'0'调用b.f2(); // f2(int)用'0'调用 (2认同)

Dav*_*eas 23

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

void write( int *optional = 0 ) {
    if (optional) *optional = 5;
}
Run Code Online (Sandbox Code Playgroud)


Mik*_*eir 11

这个小模板将帮助您:

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

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

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

private:
    T mValue;
};
Run Code Online (Sandbox Code Playgroud)

那么你将能够:

virtual const ULONG Write(ULONG &State = ByRef<ULONG>(0), bool sequence = true);
Run Code Online (Sandbox Code Playgroud)

  • @AndrewCheong 它的整个意图是在现场构建并在线路完成时销毁。这是一种在调用期间公开引用的方法,以便即使在需要引用时也可以提供默认参数。此代码用于活动项目并按预期运行。 (2认同)

Max*_*ert 6

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

我非常怀疑在现代架构上传递一个无条件的长期会让你失望太多.所以我假设您打算更改State方法内部的值.编译器抱怨,因为常量0不能更改,因为它是一个rvalue(错误消息中的"非左值")并且不可更改(const在错误消息中).

简单地说,你想要一个可以改变传递的参数的方法,但是默认情况下你想传递一个不能改变的参数.

换句话说,非const引用必须引用实际变量.函数signature(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
Run Code Online (Sandbox Code Playgroud)

如果你实际上并不打算State在方法内部进行更改,只需将其更改为a即可const ULONG&.但是你不会从中获得很大的性能优势,因此我建议将其更改为非引用ULONG.我注意到你已经回来了ULONG,我有一种偷偷摸摸的怀疑,它的值是State任何需要修改之后的价值.在这种情况下,我只需将方法声明为:

// returns value of State
virtual ULONG Write(ULONG State = 0, bool sequence = true);
Run Code Online (Sandbox Code Playgroud)

当然,我不太确定你在写什么或在哪里.但那是另一个问题.


Nas*_*rEd 5

不,不可能。

通过引用传递意味着该函数可能会更改参数的值。如果调用者未提供该参数,且该参数来自默认常量,则应更改的功能是什么?

  • 传统的FORTRAN方法本来是要更改0的值,但这在C ++中不会发生。 (3认同)

def*_*ode 5

您不能将常量文字用于默认参数的原因与您不能将其用作函数调用的参数的原因相同.参考值必须具有地址,常量参考值不需要(即它们可以是r值或常量文字).

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

foo(0); // compiler error.

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

bar(0); // ok.
Run Code Online (Sandbox Code Playgroud)

确保你的默认值有一个地址,你没事.

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;
   }
}
Run Code Online (Sandbox Code Playgroud)

我已经使用过这种模式几次,我有一个参数,可以是变量或null.常规方法是让用户传入指针,这是大小写.如果他们不希望您填写值,则传入NULL指针.我喜欢null对象方法.它使呼叫者的生活更轻松,而不会使被调用者代码变得非常复杂.