阅读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 时,持有者会复制一份?
答案 0 :(得分:21)
除非当hold_this()给出一个左值时,持有人将持有一个参考,当给予一个左值时,持有人会复制一份?
您已经写过它(减去所需的template <typename T>
)。 转发参考的扣除规则保留了值类别,如下所示:
t
绑定到T2
类型的左值,则T = T2&
。t
绑定到T2
类型的右值,则为T = T2
。 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
的构造函数参数非常重要,因为构造函数本身不是模板化的。
顺便说一下,由于您标记了c++17,我们还可以为您的示例添加演绎指南。如果我们将其定义如下(包含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()};
,其推导类型与之前的函数返回值相同。