展开 std::reference_wrapper 的成本

Posted

技术标签:

【中文标题】展开 std::reference_wrapper 的成本【英文标题】:cost of unwrapping a std::reference_wrapper 【发布时间】:2018-04-08 00:42:22 【问题描述】:

给定:

#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 行,是实际执行的间接寻址,给定特定的用例(一个包装器,它被立即解包,也没有复制,除了函数参数之外,既不绑定到本地引用),还是编译器只检测到对象是立即展开并只使用原始对象?否则哪个会是更好的设计(例如,仅保存对象副本的标量类型的部分特化是否会更有效)?

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

【问题讨论】:

你看过编译器的输出了吗?可以查看gcc生成的代码。如果没有,您可以使用godbolt.org。我不认为从std::reference_wrapper 继承通常是一个好主意。 提供一个可编译的(最小的,完整的)示例也很好。现在,代码甚至无法编译。 我同意 Jens 的观点。 std::reference_wrapper 没有任何虚拟成员。只需将其设为成员变量,而不是从其继承即可。 @MillieSmith “如果 C++ 委员会希望人们多态地 使用std::reference_wrapper,他们就会把它变成虚拟的”(我猜你的意思是析构函数是虚拟的,因为“虚拟对象”在 C++ 中没有意义)。 我同意@Peregring-lk 的观点,非多态继承没什么好丢脸的。停止攻击与问题无关的内容。 【参考方案1】:

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

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

内联是实现零开销抽象的原因,Stroustrup 喜欢提到这一点。

【讨论】:

以上是关于展开 std::reference_wrapper 的成本的主要内容,如果未能解决你的问题,请参考以下文章

是否可以仅从 std::any 使用 std::reference_wrapper 创建 std::any?

std::reference_wrapper 中的 Visual C++ 10.0 错误?

非模板 std::reference_wrapper 赋值运算符和模板构造函数

我可以实例化一个 std::reference_wrapper<T> ,其中 T 是不完整的类型吗?

多态对象的数组

在 std::map 中搜索时堆栈溢出