3

Given:

#include <iostream>
#include <functional>

template<class T> // Just for overloading purposes
struct behaviour1 : std::reference_wrapper<T const>
{
    using base_t = std::reference_wrapper<T const>;
    using base_t::base_t;

    // This wrapper will never outlive the temporary
    // if used correctly
    behaviour1(T&& t) : base_t(t) {}
};

template<class T>
behaviour1(T&&) -> behaviour1<std::decay_t<T> >;

struct os_wrapper : std::reference_wrapper<std::ostream>
{
    using std::reference_wrapper<std::ostream>::reference_wrapper;

    template<class T>
    os_wrapper& operator<<(behaviour1<T> const& v)
    {
        *this << v.get(); // #1
        return *this;
    }

    template<class U>
    os_wrapper& operator<<(U&& t)
    { this->get() << t; return *this; }
};

int main() { os_wrapper(std::cout) << behaviour1(3); }

In line #1, is the indirection actually performed, given that specific use case (a wrapper that is inmediatly unwrapped, and nor copied neither bound to local references except function parameters), or will the compiler just detect that the object is inmediatly unwrapped and just use the original object instead? Which will be a better design otherwise (for example, whether a partial specialization for scalar types that just save a copy of the object will be more efficient)?

I'm interested specifically in gcc (version 7, -O3 -std=c++17) although I guess both clang and gcc performs similar optimization techniques.

ABu
  • 10,423
  • 6
  • 52
  • 103
  • 2
    Did you take a look at the compiler output? You can check the code generated by `gcc`. If not, you could use https://godbolt.org/. I don't think that inheriting from `std::reference_wrapper` is a good idea in general. – Jens Oct 26 '17 at 15:02
  • Providing a compilable (minimal, complete) example would also be nice. Right now, the code doesn't even compile. – Jens Oct 26 '17 at 15:02
  • I agree with Jens. `std::reference_wrapper` doesn't have any virtual members. Just make it a member variable instead of inheriting from it. – Millie Smith Oct 26 '17 at 15:32
  • @MillieSmith That's a false myth. I don't mean to use any of those wrappers using pointers to base class or create dynamic memory or something. The use of the class is delimited and well defined and there's nothing wrong with inheriting from it in that case. – ABu Oct 26 '17 at 15:37
  • Code updated to a shorter form. – ABu Oct 26 '17 at 15:39
  • It's overly risky. If the C++ committee had wanted people to inherit from `std::reference_wrapper`, they would have made it virtual. I see nothing in your question that dictates that you need to inherit from it. And it's so simple to implement you might as well write your own with a virtual destructor if you're going to derive from it. – Millie Smith Oct 26 '17 at 15:43
  • 1
    @MillieSmith "If the C++ committee had wanted people to use `std::reference_wrapper` **polymorphically**, they would have made it virtual" (I guess you mean the destructor being virtual, because a "virtual object" has no meaning in C++). – ABu Oct 26 '17 at 16:02
  • 1
    I agree with @Peregring-lk here, inheriting non-polymorphically is nothing to be ashamed of. Stop attacking something that is irrelevant to the question. – Mark Ransom Oct 26 '17 at 16:05

1 Answers1

7

The expected cost of using methods of std::reference_wrapper in an optimized build is 0 because all std::reference_wrapper code can be inlined. The assembly output of statement os_wrapper(std::cout) << behaviour1(3); is:

movl    $3, %esi
movl    std::cout, %edi
call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)

Inlining is what enables zero-overhead abstractions which Stroustrup likes to mention.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271