在c ++中通过引用传递可选参数

Moo*_*min 30 c++ pass-by-reference optional-parameters

我在C++中遇到了可选函数参数的问题

我要做的是编写带有可选参数的函数,该函数通过引用传递,这样我就可以用两种方式(1)和(2),但是(2)我真的不在乎什么是的价值mFoobar.

我试过这样的代码:

void foo(double &bar, double &foobar = NULL)
{
   bar = 100;
   foobar = 150;
}

int main()
{
  double mBar(0),mFoobar(0);

  foo(mBar,mFoobar);              // (1)
  cout << mBar << mFoobar;

  mBar = 0;
  mFoobar = 0;

  foo(mBar);                     // (2)
  cout << mBar << mFoobar;

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

但它崩溃了

void foo(double &bar, double &foobar = NULL)
Run Code Online (Sandbox Code Playgroud)

有消息:

error: default argument for 'double& foobar' has type 'int'
Run Code Online (Sandbox Code Playgroud)

没有函数重载可以解决它吗?

Pot*_*ter 39

不要对可选参数使用引用.没有引用NULL的概念:引用始终是特定对象的别名.

或许看看boost::optionalstd::experimental::optional.boost::optional甚至专门用于参考类型!

void foo(double &bar, optional<double &> foobar = optional<double &>())
Run Code Online (Sandbox Code Playgroud)


In *_*ico 33

为什么不能使用函数重载?当然,这是解决您问题的最简单方法吗?

void foo(double &bar, double &foobar) 
{ 
   bar = 100; 
   foobar = 150; 
}

void foo(double &bar) 
{ 
   double foobar = 0.0;
   foo(bar, foobar);
}
Run Code Online (Sandbox Code Playgroud)

  • 我可以,但我只是想知道是否有可能解决问题而不会超载.想象一下,在人们真的讨厌超载的岛上醒来:). (17认同)
  • @Moomin我猜想"我讨厌默认参数"的岛上有更多的居民;-) (17认同)

ken*_*ytm 30

(可变)引用的默认参数必须是l值.我能想到的最好,没有超载,是

static double _dummy_foobar;
void foo(double &bar, double &foobar = _dummy_foobar)
Run Code Online (Sandbox Code Playgroud)

  • 如果读取虚拟对象,这可能会引入细微的线程安全问题. (5认同)
  • 当然,它回答了有限程度的问题,但是我质疑问题背后的动机......当C++已经提供了可空/可检查的替代间接时,为什么还要分配一个无用的虚拟变量呢?它被称为指针! (4认同)
  • 虽然这是一个很好的答案,但我没有看到与使用指针相比的优势。如果您有一个可选的指针参数,并且您尝试访问它,则会收到运行时错误。如果您使用虚拟参考,则不会。您可能正在与虚拟对象进行交互,但您并不知道。我宁愿有一个运行时错误来通知我我做错了什么。现在,您可以将其与虚拟变量进行比较,看看该参数是否确实已指定;但随后您可以测试指针是否非零。关键是,检测错误是针对那些您忘记测试的情况。 (3认同)
  • @pkh这个定义很常见,但错了.一些右值也可以分配给,例如`std :: string("hello")= world`,并且一些左值不能分配给,例如C-数组和没有赋值运算符的对象.(当然,也不能指定常数值,但这似乎很好理解.) (2认同)

Raj*_*ain 6

要使用标准库安全地执行此操作,您需要std::optional结合使用std::reference_wrapper. 形式的可选引用std::optional<T&>在 C++17 中是非法的。

#include <optional>
#include <functional>

void foo(double& bar, std::optional<std::reference_wrapper<double>> foobar = {})
{
    if(foobar) // If the user has passed a reference
    {
        foobar->get() = 1.0; // Assign values to the reference
    }
}
Run Code Online (Sandbox Code Playgroud)

这对于被调用者来说是完全透明的。您可以像平常一样调用这样的函数:

double a {}, b {};
foo(b, a);
std::cout << a; // Prints 1.0;
Run Code Online (Sandbox Code Playgroud)

这种方法的优点是它有一个空值来指示用户是否确实传递了引用。使用-O2/-O3,它还可以非常巧妙地进行优化,并且没有运行时成本。


pkh*_*pkh 5

另一种方法是使用指针而不是引用.这提供了您想要的语义而不会重载.(就个人而言,我可能会超载.)

void foo(double* bar, double* foobar = 0)
{
   if (bar) *bar = 100;
   if (foobar) *foobar = 150;
}

   // ...

   foo(&mBar, &mFoobar);

   // ...

   foo(&mBar);

   // ...
Run Code Online (Sandbox Code Playgroud)

  • 这导致了一堆!= || == nullptr洒遍布宇宙,一个巨大的头痛+延迟在实际上得到当程序员阅读与nullptr和您的20,000个行库工作*******洋洋洒洒不能找出谁拥有什么,什么需要指定,什么不需要,并且有轻微的中风. (3认同)
  • 我听到你在说什么,但是你会超过它.就像我说的,我的偏好通常是使用重载,但我经常从那些基于公共引用的重载函数调用到一个公共的私有函数来完成实际的工作,我很高兴通过指针和执行我的null检查在. (2认同)

Cas*_*son 5

这是另一种不会导致内存泄漏的疯狂方法,您在现实生活中永远不应该使用它,但乍一看似乎符合标准,并在 Cygwin 下使用 Visual C++ 2008 & g++ 3.4.4 进行编译:

void foo(double &bar, double &foobar = (*((double*)0)))
{
   bar = 100;
   double* pfoobar = &foobar;
   if (pfoobar != 0)
       foobar = 150;
}
Run Code Online (Sandbox Code Playgroud)

重申:不要这样做!有更好的选择!超载可以成为你的朋友!但是,是的,如果您既愚蠢又小心,就可以做到。:)