Superconstructing super elider, round 2

Remember my suggestion from “The Superconstructing Super Elider” that maybe our containers ought to have a “directly-emplacing, never-moving” facility like this?

template<class F>
T& emplace_back_with_result_of(const F& factory_fn) {
    assert(size_ < Cap);
    T *p = new ((void*)buffer_[size_]) T(factory_fn());
    size_ += 1;
    return *p;
}

Well, today I learned that Andrzej Krzemieński knows how to get this effect without modifying the container. The trick is that when we write

new (voidptr) T(some_value)

we aren’t just invoking T’s constructor; we might also be invoking some implicit conversion on some_value. And we can overload the behavior of that implicit conversion! Thus:

template<class F>
class with_result_of_t {
    F&& fun;
public:
    using T = decltype(std::declval<F&&>()());
    explicit with_result_of_t(F&& f) : fun(std::forward<F>(f)) {}
    operator T() { return fun(); }
};

template<class F>
inline with_result_of_t<F> with_result_of(F&& f) {
    return with_result_of_t<F>(std::forward<F>(f));
}

Notice the overloaded operator T. Now we can write:

std::vector<Widget> vec;

vec.emplace_back(Widget::make());  // A

vec.emplace_back(with_result_of([]{ return Widget::make(); }));  // B

Line A produces an extra move-construction of Widget. Line B constructs the return value of Widget::make() directly into its final place in the vector; there is no extra movement at all.

This mechanism is super clever. But I must note that it is not 100% foolproof. It works more or less because overload resolution finds Widget(Widget) to be the best match for construction from with_result_of_t<F>. If Widget were to have a constructor that were a better match for with_result_of_t<F>, then the mechanism would fail:

struct Widget {
    int value_;
    explicit Widget(int i) : value_(i) { puts("  construct from int"); }
    Widget(Widget&&) noexcept { puts("  move-ctor"); }
    ~Widget() { puts("  destructor"); };
    static Widget make() { return Widget(42); }

    template<class U> explicit Widget(U&&) : value_(0) { printf("  %s -- OOPS!\n", __PRETTY_FUNCTION__); }
};

So this trick does have at least one sharp edge. But it’s pretty cool that you can get this effect if you need it, even without abandoning the standard containers!

Posted 2018-05-17