展开 std::reference_wrapper 的成本

cost of unwrapping a std::reference_wrapper

鉴于:

#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); }

在第 1 行中,是否实际执行了间接寻址,给定了特定的用例(立即解包的包装器,并且没有复制,除了函数参数之外,既没有绑定到本地引用),还是编译器会检测到object 立即展开并只使用原始对象?否则哪个设计会更好(例如,仅保存对象副本的标量类型的部分特化是否更有效)?

我对 gcc(版本 7,-O3 -std=c++17)特别感兴趣,尽管我猜想 clanggcc 都执行类似的优化技术。

在优化构建中使用 std::reference_wrapper 方法的预期成本为 0,因为所有 std::reference_wrapper 代码都可以内联。 The assembly output of statement os_wrapper(std::cout) << behaviour1(3); is:

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

内联可以实现 Stroustrup 喜欢提到的零开销抽象。