提问者:小点点

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


阅读有关通用引用的内容使我产生了疑问:我如何构造一个类模板,使它能够在可能的情况下按引用存储,或者在必须的情况下按值存储?

那就是,我可以做这样的事情吗

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()被赋予L值时,持有者将持有引用,而当被赋予R值时,持有者将复制?


共1个答案

匿名用户

除了当给hold_this()一个L值时,持有者将持有一个引用,而当给出一个R值时,持有者将制作一个副本?

您已经编写了它(减去所需的模板)。 转发参照保值类别的扣减规则如下:

  1. 如果T绑定到T2类型的L值,则T=T2&
  2. 如果T绑定到T2类型的rvalue,则T=T2

std::forward依靠这些演绎规则来完成工作。 以及为什么我们还需要将类型传递给它。

上面的意思是在rvalue的情况下直接用t2实例化holder。 给你你想要的东西。 复制一份。

事实上,一式两份。 一次是创建构造函数参数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)); }

看现场直播。 我们使用add_rvalue_reference_t使t在每种情况下都具有正确的引用类型。 并“模拟”参数推导,使OBJ_M{std::forward(t)}解析为从正确的引用类型初始化OBJ_M

我说“模拟”是因为理解构造函数参数非常重要,holder不能是转发引用,因为构造函数本身没有模板化。

顺便说一下,既然你给C++17加了标签,我们也可以给你的例子添加一个演绎指南。 如果我们将其定义如下(根据T.C.incorporated的反馈):

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>;

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