Moomin
Moomin

Reputation: 1926

Passing optional parameter by reference in c++

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)
  std::cout << mBar << mFoobar;

  mBar = 0;
  mFoobar = 0;

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

  return 0;
}

but this line doesn't compile

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?

Upvotes: 57

Views: 95563

Answers (13)

Bobby_Zero
Bobby_Zero

Reputation: 1

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<QString>().get()));

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

Upvotes: -2

Burak
Burak

Reputation: 2495

We want temporary materialization to occur.

Possible options:

  • bind a reference to a prvalue

    See my other answer.

  • perform member access

    double& foobar = *std::make_unique<double>(0.0);
    
  • perform array to pointer conversion

    double& foobar = std::array<double, 1>{ 0.0 }[0];
    

Upvotes: 0

Burak
Burak

Reputation: 2495

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.

Upvotes: 1

Mike Kaganski
Mike Kaganski

Reputation: 821

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.

Upvotes: 0

Rajat Jain
Rajat Jain

Reputation: 482

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.

Upvotes: 8

wdavilaneto
wdavilaneto

Reputation: 818

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...

Upvotes: 0

Potatoswatter
Potatoswatter

Reputation: 137810

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 &>())

Upvotes: 44

Zorayr
Zorayr

Reputation: 24922

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

Upvotes: 2

CasaDeRobison
CasaDeRobison

Reputation: 407

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. :)

Upvotes: 5

pkh
pkh

Reputation: 3749

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

   // ...

Upvotes: 12

faya
faya

Reputation: 5715

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! :))

Upvotes: -4

In silico
In silico

Reputation: 52159

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

Upvotes: 55

kennytm
kennytm

Reputation: 523294

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)

Upvotes: 50

Related Questions