开发者

Passing optional parameter by reference in c++

开发者 https://www.devze.com 2022-12-30 04:02 出处:网络
I\'m having a problem with optional function parameter in C++ What I\'m trying to do is to write function with optional parameter which is passed by reference, so that I can use it in two ways (1) an

I'm having a problem with optional function parameter in C++

What I'm trying to do is to write function with optional parameter which is passed by reference, so that I can use it in two ways (1) and (2), but on (2) I don't really care what is the value of mFoobar.

I've tried such a code:

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

but i开发者_StackOverflow中文版t crashes at

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

with message :

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

Is it possible to solve it without function overloading?


Why can't you use function overloading? Surely it's the easiest solution to your problem?

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

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


Don't use references for optional parameters. There is no concept of reference NULL: a reference is always an alias to a particular object.

Perhaps look at boost::optional or std::experimental::optional. boost::optional is even specialized for reference types!

void foo(double &bar, optional<double &> foobar = optional<double &>())


The default argument of a (mutable) reference must be an l-value. The best I can think of, without overloading, is

static double _dummy_foobar;
void foo(double &bar, double &foobar = _dummy_foobar)


Another way to do this is to use pointers instead of references. This provides the semantics that you want without overloading. (Personally, I'd probably go with overloading.)

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

   // ...

   foo(&mBar, &mFoobar);

   // ...

   foo(&mBar);

   // ...


Here is another crazy way that does not result in memory leaks, which you should never use in real life, but seems to be standard compliant at first glance and compiles with Visual C++ 2008 & g++ 3.4.4 under Cygwin:

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

To reiterate: DON'T DO THIS! THERE ARE BETTER OPTIONS! OVERLOADING CAN BE YOUR FRIEND! But yeah, you can do it if you're foolish and careful. :)


To do this safely with the standard library, you'll need to use std::optional in conjunction with std::reference_wrapper. Optional references of the form std::optional<T&> are illegal in 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
    }
}

This is completely transparent to the callee. You can call such a function as you would normally:

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

The advantage of this approach is that it has a null value to indicate if the user has actually passed a reference or not. With -O2/-O3, it also optimises away pretty neatly with no runtime cost.


It is much easier to use a pointer type and setting it to NULL than setting default/optional value for a reference parameter.


void foo(double &bar,
         double &foobar = const_cast<double &>(static_cast<const double &>(0.0)))
{
   bar = 100;
   foobar = 150;
}

What happens here is that we first create a temporary variable at value 0.0. Using static_cast, we obtain a reference, and the life of the temporary is extended. We cannot directly cast to a mutable at this stage. Since the original temporary is not marked as const, we can have a mutable reference by using const_cast, which is not advised to be used in general but suitable for this case. By assigning the result to a named variable called foobar, there occurs life extension, thus we can have an optional output parameter without function overloading.


I agree that the previous solution is ugly and too verbose. There is a better approach which has the same logic behind. With C++11, we can benefit from r-value reference (&&).

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

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

See understanding l-values and r-values for a good explanation with simple examples.


Speaking in terms of Object Oriented paradigm: If given class has and "Default", this Default must declared accordingly, and then may be used as an "Default Parameter" Ex:

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

On your Method ...

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

Edited: This solution is quite suitable since in this case it is an "global default" Pagination and a single "reference" value. You will also have the power to change default values such as navigation/display preferences and etc.

Edit2: spelling and fixing...


Starting with C++17, you may use parameter pack to implement this without manual overload, and without runtime overhead, like this:

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
}

No "is this argument provided" check would be needed at runtime, the fold expression will expand to the code only for invocations actually passing the argument to the function.

Alternatively to the static_assert, the check for the argument correctness may be inplemented using std::enable_if.


This is how I solved this question:

My original function didn't have a returned error string: bool MyClass::validateXML(const QString& fileName, const QUri& schemaUri);

I wanted to add the results of the validation in an error string so I implemented: bool MyClass::validateXML(const QString& fileName, const QUri& schemaUri, QString& errorString = *(std::make_unique().get()));

This way, you can reference the errorString in validateXML without checking if it's valid, and no memory leaks.


You can do this crazy way:

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

P.S. - I know its not pleasant but its the way. Also be sure not to leave memory leaks! :))

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号