44

我在 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;
}

但它崩溃了

void foo(double &bar, double &foobar = NULL)

带有消息:

error: default argument for 'double& foobar' has type 'int'

是否可以在没有函数重载的情况下解决它?

4

12 回答 12

45

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

也许看看boost::optionalstd::experimental::optionalboost::optional甚至专门用于引用类型!

void foo(double &bar, optional<double &> foobar = optional<double &>())
于 2010-05-12T05:57:20.063 回答
44

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

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

void foo(double &bar) 
{ 
   double foobar = 0.0;
   foo(bar, foobar);
}
于 2010-05-12T05:56:42.137 回答
41

(可变)引用的默认参数必须是左值。在不超载的情况下,我能想到的最好的方法是

static double _dummy_foobar;
void foo(double &bar, double &foobar = _dummy_foobar)
于 2010-05-12T05:53:57.867 回答
9

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

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

   // ...

   foo(&mBar, &mFoobar);

   // ...

   foo(&mBar);

   // ...
于 2010-05-12T06:00:58.147 回答
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;
}

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

于 2011-03-23T18:15:42.960 回答
2

要使用标准库安全地执行此操作,您需要std::optionalstd::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
    }
}

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

double a {}, b {};
foo(b, a);
std::cout << a; // Prints 1.0;

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

于 2021-06-21T14:14:35.713 回答
1

使用指针类型并将其设置为 NULL 比为引用参数设置默认/可选值要容易得多。

于 2012-05-31T21:57:51.810 回答
1
void foo(double &bar,
         double &foobar = const_cast<double &>(static_cast<const double &>(0.0)))
{
   bar = 100;
   foobar = 150;
}

这里发生的是我们首先在 value 处创建一个临时变量0.0。使用static_cast,我们获得一个引用,并且临时的生命被延长。在这个阶段我们不能直接转换为一个可变的。由于原始临时没有标记为const,我们可以通过 using 来获得可变引用const_cast,一般不建议使用,但适合这种情况。通过将结果分配给名为 的命名变量foobar,可以延长寿命,因此我们可以有一个可选的输出参数而无需函数重载。


我同意以前的解决方案丑陋且过于冗长。有一种更好的方法,其背后具有相同的逻辑。使用 C++11,我们可以从 r 值引用 ( &&) 中受益。

template <class T>
T& make_ref(T&& x) { return x; }

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

请参阅理解 l 值和 r 值以通过简单示例获得很好的解释。

于 2020-10-23T14:21:17.467 回答
0

就面向对象范式而言:如果给定的类具有“默认值”,则必须相应地声明此默认值,然后可以将其用作“默认参数” 例如:

class Pagination {
private:
    int currentPage;
public:

    //...
    Pagination() {
        currentPage = 1;
        //...
    }

    // your Default Pagination (Must be initialized before thread concurrency)
    static Pagination& Default() {
        static Pagination p; 
        return p;
    }
};

在你的方法...

     //...
     std::vector<User>
     findByFilter(User& audit, Pagination& p = Pagination::Default() ) {
     // ...

已编辑:此解决方案非常合适,因为在这种情况下,它是“全局默认”分页和单个“参考”值。您还可以更改默认值,例如导航/显示首选项等。

Edit2:拼写和修复...

于 2014-04-30T16:24:32.670 回答
0

从 C++17 开始,您可以使用参数包来实现这一点,而无需手动重载,也无需运行时开销,如下所示:

template <typename... OptArgType> void foo(double &bar, OptArgType&... foobar)
{
   static_assert(sizeof...(OptArgType) <= 1 && (std::is_same_v<OptArgType, double> && ...));
   bar = 100;
   ((foobar = 150), ...); // use fold expression for assignment
}

在运行时不需要“是否提供此参数”检查,折叠表达式将扩展为代码,仅用于实际将参数传递给函数的调用。

或者,static_assert可以使用 来实现对参数正确性的检查std::enable_if

于 2021-10-04T10:20:08.713 回答
-2

这就是我解决这个问题的方法:

我的原始函数没有返回错误字符串: bool MyClass::validateXML(const QString& fileName, const QUri& schemaUri);

我想在错误字符串中添加验证结果,所以我实现了: bool MyClass::validateXML(const QString& fileName, const QUri& schemaUri, QString& errorString = *(std::make_unique().get()));

这样,您可以在 validateXML 中引用 errorString 而无需检查它是否有效,也不会发生内存泄漏。

于 2018-12-04T22:24:53.867 回答
-4

你可以这样做疯狂的方式:

void foo(double &bar, double &foobar = (*(new double())))

PS - 我知道它不愉快,但它的方式。还要确保不要留下内存泄漏!:))

于 2010-05-12T05:59:33.283 回答