4

Suppose I have some base class which can optionally return me some specific data. It also provides me 'hasData' function to check if such specific data is available for usage

class MyClassBase {
public:
    virtual bool hasData() const { return false; }
    virtual const Arg1& getData() const { throw std::runtime_error("No data");  }
};

class MyClassDerived: public MyClassBase {
    Arg1 m_data = Arg1(10);
public:
    bool hasData() const override { return true; }
    // Good - no copy constructor for data as I want
    const Arg1& getData() const override { return m_data; }
};

This works well and does what I want. But 'hasData' and 'getData' are good candidates to be replaced by one function returning 'std::optional'. But when I tried to improve API returning std::optional, I realized that I can't return a 'const reference' anymore to my internal data

class MyClassWithOptBase {
public:
    virtual std::optional<Arg1> getData() const { return std::nullopt; }
};

class MyClassWithOptDerived: public MyClassWithOptBase  {
    Arg1 m_data = Arg1(10);
public:
    // Bad - copy constructor is created for my data!
    std::optional<Arg1> getData() const override { return m_data; }

    // std::optional<const Arg1 &> - doesn't compile as references are not allowed
    // const std::optional<Arg1> & - can't return reference to temporary object
};

One possibility is to use std::optional<Arg1> m_data MyClassWithOptDerived - but it doesn't look nice for me - derived class definitely has data and there shall be no reason to store std::optional in it. Also there will be need to move 'm_data' to base class which I definitely don't want

Any other possibility to use std::optional in such example and avoid copying of data?

PS: I checked some articles like std::optional specialization for reference types and seems that it is not possible to avoid copy of data and I probably should live with 'old-style' interface here.

Update: Thank you everyone for such quick responses. Solution that works for me is to use std::reference_wrapper. The solution code will look like

class MyClassWithOptBase {
public:
    virtual std::optional<std::reference_wrapper<const Arg1>> getData() const {
        return std::nullopt;
    }
};

class MyClassWithOptDerived : public MyClassWithOptBase {
    Arg1 m_data = Arg1(10);
public:
    // Good as well - no copy constructor for my data!
    std::optional<std::reference_wrapper<const Arg1>> getData() const override {
        return m_data;
    }
};

// In 'main'
MyClassWithOptDerived opt;
auto res = opt.getData();
//Then res->get() will return me 'const Arg1&' as I want and no copy constructor will be invoked
Michael Nosov
  • 61
  • 1
  • 4
  • the link in the PS already answers your question, no? – 463035818_is_not_an_ai Sep 07 '20 at 08:00
  • 1
    What about `std::optional>`? It's mentioned in the post you linked. What is wrong with it? – Daniel Langr Sep 07 '20 at 08:04
  • @idclev463035818, yes, partially. Looks like with C++17 it is not possible to do what I want. But maybe there is some initiative to support it in future C++ versions... Maybe there are some plans to introduce some new type like std::optional_cref which will not allow reassiging or resetting, but also will make it possible to return const-reference and don't copy actual data – Michael Nosov Sep 07 '20 at 08:09
  • @MichaelNosov Why "_partially_"? – Daniel Langr Sep 07 '20 at 08:10
  • my naive understanding is that `std::optional` is for cases where you specifically want the object to be stored inside the `std::optional`, while your case could be handled by returning a (possibibly null) pointer – 463035818_is_not_an_ai Sep 07 '20 at 08:11
  • @DanielLangr, Thanks a lot! This way with reference_wrapper works good, looks like I missed this in the PS – Michael Nosov Sep 07 '20 at 08:15
  • 1
    please do not add solutions/answers to the question. Answers should go to answers. You basically vandalized your own question, it isnt answerable anymore, because it already contains its own answer. Imho [this question](https://stackoverflow.com/questions/26858034/stdoptional-specialization-for-reference-types) needs an answer that mentions `reference_wrapper` then there would be no doubt that this one is a duplicate – 463035818_is_not_an_ai Sep 07 '20 at 08:29
  • 1
    you can post an answer to your own question instead of including your solution to the quesiton – 463035818_is_not_an_ai Sep 07 '20 at 08:29

1 Answers1

2

An optional reference is the same as a pointer. Return const Arg1 *.

Caleth
  • 52,200
  • 2
  • 44
  • 75
  • 1
    However, there is a difference between _undefined behavior_ (possible invalid pointer dereferencing) and _throwing an exception_. – Daniel Langr Sep 07 '20 at 08:12
  • @DanielLangr My assumption is that base would return `nullptr` instead of `nullopt`, and testing for null would replace testing for empty. The first implementation that throws is discarded – Caleth Sep 07 '20 at 08:14
  • But you need to test for `nullptr` everywhere where you access the returned pointer, which may be error-prone in future code maintenance. With [`std::optional::value()`](https://en.cppreference.com/w/cpp/utility/optional/value), you just need to catch exceptions. – Daniel Langr Sep 07 '20 at 08:16
  • @Caleth I guess Daniel refers to using the value without testing for empty which causes UB for the pointer but an exception with `std::optional` – 463035818_is_not_an_ai Sep 07 '20 at 08:16