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

时间:2009-06-29 18:05:29

标签: c++ pass-by-reference default-value

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

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

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

当我这样做时,它会出错:

  

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

17 个答案:

答案 0 :(得分:91)

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

这样做的一种方法是使用实​​际实例作为默认实例:

static int AVAL = 1;

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

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

但实际使用非常有限。

答案 1 :(得分:28)

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

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
}

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

答案 2 :(得分:22)

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

void write( int *optional = 0 ) {
    if (optional) *optional = 5;
}

答案 3 :(得分:11)

这个小模板可以帮到你:

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

答案 4 :(得分:6)

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

我非常怀疑在现代架构上传递一个无条件的长期会让你失望太多。所以我假设您打算在方法中更改State的值。编译器抱怨,因为常量0无法更改,因为它是一个rvalue(错误消息中的“非左值”)并且在错误消息中不可更改(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 :(得分:5)

您不能将常量文字用于默认参数的原因与您不能将其用作函数调用的参数的原因相同。参考值必须有一个地址,常量参考值不需要(即它们可以是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指针。我喜欢null对象方法。它使呼叫者的生活更轻松,而不会使被调用者代码变得非常复杂。

答案 6 :(得分:4)

不,这是不可能的。

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

答案 7 :(得分:1)

我认为不是,原因是默认值被计算为常量,并且通过引用传递的值必须能够更改,除非您还声明它是常量引用。

答案 8 :(得分:1)

使用const限定符可以使用State:

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

答案 9 :(得分:1)

在OO的情况下......要说给定类具有和“默认”意味着必须声明此默认值(值),然后可以将其作为默认参数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() ) {

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

答案 10 :(得分:1)

void f(const double& v = *(double*) NULL)
{
  if (&v == NULL)
    cout << "default" << endl;
  else
    cout << "other " << v << endl;
}

答案 11 :(得分:1)

另一种方式可能如下:

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"

答案 12 :(得分:0)

void revealSelection(const ScrollAlignment& = ScrollAlignment::alignCenterIfNeeded, bool revealExtent = false);

答案 13 :(得分:0)

这也有相当肮脏的技巧:

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

在这种情况下,您必须使用std::move

进行调用
ULONG val = 0;
Write(std::move(val));

这只是一些有趣的解决方法,我完全不建议在实际代码中使用它!

答案 14 :(得分:0)

我对此有一种解决方法,请参见以下示例,了解int&的默认值:

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

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

}

您可以为所需的任何普通数据类型执行此操作,例如booldouble ...

答案 15 :(得分:0)

定义2个重载函数。

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

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

答案 16 :(得分:-3)

  

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

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

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