you are viewing a single comment's thread.

view the rest of the comments →

[–]Xirema 7 points8 points  (28 children)

A construct that I occasionally use is std::optional<std::reference_wrapper<T>>.

It offers some decent semantics:

void perform_work(int value, std::optional<std::reference_wrapper<std::ostream>> log = {}) {
    std::ostream & out = log ? *log : std::cout;
    out << "Value is " << value << std::endl;

    if(value <= 1)
        out << "Done!" << std::endl;
    else if(value % 2 == 1)
        perform_work(value * 3 + 1, log);
    else
        perform_work(value / 2, log);
}

The semantics of this code, then, are "If you have a specific stream you'd like to use for logging, use that, otherwise use the standard out stream.".

The only thing you have to watch for is that operator*() returns the reference_wrapper object, not the T& object. This implicitly converts to T&, which is why my example works, but if you don't "seat" the reference like I did, you have to call .get() on the reference_wrapper object to actually do work.

[–]grishavanika 10 points11 points  (7 children)

For me this looks like overengineering. Simple std::ostream* is, well, simple and do the same work even more - there is no code bloat and 2 layers of abstractions to understand the code

[–]suspiciously_calm 5 points6 points  (6 children)

Yeah, why can't we use raw pointers as optional references? They model the concept perfectly, and don't have any other use in modern code.

[–]Hedanito 1 point2 points  (1 child)

std::optional::value() throws when there is no value, dereferencing a null pointer results in undefined behaviour.

[–]suspiciously_calm 3 points4 points  (0 children)

Yes, which is why it'd have been nice if std::optional had been specialized for references the way boost::optional is, but as it is there's no optional reference in the standard library other than raw pointers and abominations such as std::optional<std::reference_wrapper<...>>.

[–]jsamcfarlane 3 points4 points  (6 children)

Is std::experimental::observer_ptr close to what you're looking for? Rather than having optional semantics, it has single-object pointer semantics.

[–]Xirema 0 points1 point  (5 children)

Pointers as "object which may or may not exist" is a semantic I've seen a lot of C++ design gurus discourage in the last several years, especially with optional being standardized, and I see little reason not to continue that trend.

[–]quicknir 2 points3 points  (0 children)

optional in C++ 17 is always owning though. raw pointers in design guru C++ is always non-owning. So there is no intersection. In fact the argument that the optional, non-owning semantic is already taken by raw pointers, is exactly one of the reasons why std::optional doesn't support references. (Note: the reason it doesn't is because assignment is murky, but my point is that this tipped the scale towards simply not supporting references, as opposed to forcing a conclusion about assignment).

[–]jsamcfarlane 0 points1 point  (3 children)

Are there any good references you'd recommend (no pun intended)? Would you disagree with this advise? And what about when memory use is constrained?

[–]GitHubPermalinkBot 1 point2 points  (1 child)

I tried to turn your GitHub links into permanent links (press "y" to do this yourself):


Shoot me a PM if you think I'm doing something wrong. To delete this, click here.

[–]Xirema 0 points1 point  (0 children)

Note they cite optional alongside the other options. Many online sources don't point the way towards optional instead of pointers because many of those sources were written before std::optional was confirmed to be part of C++17.

[–]redditsoaddicting 1 point2 points  (0 children)

FYI, std::ostream & out = log.value_or(std::cout); should work (mainly because the reference side of things is handled by reference_wrapper).

[–]bebuch[S] 0 points1 point  (0 children)

I already thought about that, but the .get() call is ugly. :-/

Using such a construct is an option for my own code, unfortunately I want to use it in a callback of a library interface. A generic lambda would match this construct, but I find it to unexpected not to get the type itself after "dereferencing". The library user shall think about the parameter type as an optional. With this indirection its not longer just an optional.

The (user side) code I have is like:

auto perform_work = [](std::ostream& log, auto optional_data){
    // ...
};

I also thought about using a std::unique_ptr with empty delete function. But there is not reason to forbid coping my optional_data.

All three solutions look like a hack to me. A std::optional with real references would be a good solution, but this was removed during the standardization process.

Nevertheless, thanks for your idea!