0
votes

My C++ compiler is C++ 14. I have a function which intakes an object of a custom class which encapsulates all kinds of error codes. This parameter is passed by reference. The custom class object sometimes is populated with a valid error value & at times it can be empty. I found a nice looking new thing in C++14 that is std::experimental::optional. Now I am trying to use this for my parameter as it is really an optional parameter.

Following is my function's signature with std::experimental::optional that I am trying to use:

MyFunction(some param, std::experimental::optional<MyCustomErrorClass> & error_code) {

    //some logic
    //sometimes error_code is populated & sometimes not populated
}

Following is how I am calling MyFunction:

MyCustomErrorClass error_code_object;
MyFunction(some_param, error_code_object);

But I receive the following compiler error:

error: non-const lvalue reference to type 'std::experimental::optional< MyCustomErrorClass>' cannot bind to a value of unrelated type 'MyCustomErrorClass' MyFunction(some_param, error_code_object);

I tried searching this a lot. Most of the std::experimental::optional or std::optional usage examples demonstrate it as a return value of functions.

What is wrong with my usage of std::experimental::optional ?

3
Why is error_code passed by non-const reference? If it's purely an input parameter, pass it by const-ref just as you would with any other parameter... - ildjarn
C++14 is a standard. When describing your compiler please use its name, version number and platform. - Lightness Races in Orbit
@LightnessRacesinOrbit Sorry about that. My code is compiled on clang, gcc & microsoft windows. all 3. - TheWaterProgrammer
@ildjarn it passed as a reference because is an out param. it is supposed to be populated & its value changed my MyFunction when error occurs - TheWaterProgrammer
@SegmentationFault: But the variable in the calling scope isn't an optional. So how is the optional out param supposed to be accessed in the calling scope? - Lightness Races in Orbit

3 Answers

2
votes

This is not about std::experimental::optional; it is about attempting to bind a temporary to an lvalue reference, which is not possible.

That temporary is the result of the implicit conversion from MyCustomErrorClass to optional<MyCustomErrorClass>.

A proper MCVE for your problem follows:

void f(long&);

int main()
{
   int x = 42;
   f(x);
}

// error: invalid initialization of non-const reference of type 'long int&' from an
//          rvalue of type 'long int'

Implicit conversion between int and long is possible, but the resulting temporary cannot bind to an lvalue reference.

Instead, pre-construct the optional object and pass it in by name.

optional<MyCustomErrorClass> opt_error_code_object = MyCustomErrorClass{};
MyFunction(some_param, opt_error_code_object);
2
votes

You do not want to take an optional by reference here.

A reference to an optional is a very different beast that an optional reference; the second was not voted into C++17 because it has confusing semantics, but it would be closer to what you want.

An optional stores a copy of its data within itself. A reference to an optional is thus a reference to a container containing a copy of the object.

When you pass your error object to the function expecting a reference to an optional of your error type, it first copies the error object into a temporary optional. Then it tries to bind that temporary to a (lvalue) reference; which is illegal. So your code fails to compile.

If you "fix" it by creating a non-temporary optional, or casting the temporary to an lvalue, you probably still do not get what you want, because the function now interacts with the copy inside the optional, not the original error object.

Typically when you pass an object by reference, you want changes to propogate out. But they won't propogate beyond the copy inside the optional.

A solution is to take a MyCustomErrorClass* -- a pointer. This is nullable (like an optional), and does not store a copy. Simply add a & at the calling site, and use nullptr when you do not want to pass it.

1
votes

You're not giving your function an optional MyCustomErrorClass, you're giving it a non-optional MyCustomErrorClass. And you're giving it by reference, which means you can try to mutate it and it'll try to mutate the object passed in. But you can't perform mutable optional<T> operations on something that actually isn't optional<T> (well, I suppose you could if you really, really wanted to... but bad things happen to people who do that sort of thing).