类模板如何存储引用或值?

时间:2018-02-19 08:57:36

标签: c++ templates c++17

阅读universal references让我想知道:我如何构建一个类模板,以便在可能的情况下通过引用存储,或者如果必须,则按值存储?

也就是说,我可以做这样的事吗

template <class T>
class holder {
    T  obj_m;  // should be a reference if possible...
public:
    holder(T t) :obj_m { t } {}
}

auto 
hold_this(T && t) { return holder<T>(t); }

除了hold_this()给出左值时,持有人将持有一个引用,当给出 rvalue 时,持有者会复制一份?

1 个答案:

答案 0 :(得分:21)

  

除非当hold_this()给出一个左值时,持有人将持有一个参考,当给予一个左值时,持有人会复制一份?

您已经写过它(减去所需的template <typename T>)。 转发参考的扣除规则保留了值类别,如下所示:

  1. 如果t绑定到T2类型的左值,则T = T2&
  2. 如果t绑定到T2类型的右值,则为T = T2
  3. std::forward依赖于这些扣除规则来完成其工作。为什么我们也需要将类型传递给它。

    以上表示您在rvalue案例中直接使用holder实例化T2。准确地给你你想要的东西。制作副本。

    事实上,制作了两份。一次创建构造函数参数t,另一个副本是从中初始化obj_m。但我们可以通过巧妙地使用type_traits来摆脱它:

    template <class T>
    class holder {
        T  obj_m;  // should be a reference if possible...
    public:
        holder(std::add_rvalue_reference_t<T> t) :obj_m { std::forward<T>(t) } {}
    };
    
    template<typename T>
    auto hold_this(T && t) { return holder<T>(std::forward<T>(t)); }
    

    See it live。我们使用add_rvalue_reference_t使t在每种情况下都具有正确的引用类型。并且&#34;模拟&#34;参数推导使obj_m { std::forward<T>(t) }解析为从正确的引用类型初始化obj_m

    我说&#34;模拟&#34;因为理解holder的构造函数参数非常重要,因为构造函数本身不是模板化的。

    顺便说一下,由于您标记了,我们还可以为您的示例添加演绎指南。如果我们将其定义如下(包含feedback from T.C.):

    template <class T>
    class holder {
        T  obj_m;  // should be a reference if possible...
    public:
        holder(T&& t) :obj_m { std::forward<T>(t) } {}
    };
    
    template<typename T>
    holder(T&&) -> holder<T>;
    

    然后this live example显示您可以将变量定义为hold h1{t};hold h2{test()};,其推导类型与之前的函数返回值相同。