2

Consider this. There is a non-copyable, non-movable class, and there is some predicated defined for it:

struct AA
{
    AA(AA const&  otehr) = delete;
    AA(AA      && otehr) = delete;
    AA& operator = (AA const&  otehr) = delete;
    AA& operator = (AA      && otehr) = delete;

    AA(int something) { }

    bool good() const { return false; }
};

Because of guaranteed copy/move-elision in C++17 we can have:

auto getA() { return AA(10); }

The question is: how can one define getGoodA, that will forward getA in case it returned good and will throw an exception otherwise? Is it possible at all?

auto getGoodA()
{
    auto got = getA();
    if (got.good()) return got; // FAILS! Move is needed.
    throw std::runtime_error("BAD");
}
Vahagn
  • 4,670
  • 9
  • 43
  • 72

1 Answers1

2

If we had got contract checking in C++20 you would have been able to write something like:

auto getGoodA(int i) [[post aa: aa.good()]] {
    return getA(i);
}

(At least I think so - I'm not entirely clear on the type of the aa return pseudo-variable; it would need to be a reference to the returned object in its return location.) Unfortunately contracts were removed from C++20 so it'll be some time before we can write this.

Assuming you can't modify getA, the only way for now is to return a wrapper class from getGoodA. The obvious solution would be unique_ptr, but we don't actually need to perform heap allocation; a delayed-construction wrapper will do just as well:

#include <cstddef>
#include <new>
struct BB {
    alignas(AA) std::byte buf[sizeof(AA)];
    template<class F, class G> BB(F f, G g) { g(*new (buf) AA{f()}); }
    BB(BB&&) = delete;
    ~BB() { reinterpret_cast<AA&>(buf).~AA(); }
    operator AA&() { return reinterpret_cast<AA&>(buf); }
    operator AA const&() const { return reinterpret_cast<AA const&>(buf); }
};
auto getGoodA(int i) {
    return BB{
        [&] { return getA(i); },
        [&](AA& aa) { if (!aa.good()) throw (struct bad**){}; }};
}

Here I've given BB a reference-style interface, allowing you to write AA& aa = getGoodA(i), but you could equally give it a pointer-style interface (operator* and operator->) or even copy the interface of AA.

ecatmur
  • 152,476
  • 27
  • 293
  • 366