如何在模板中存储左值或右值引用

How to store either rvalue or lvalue references in template

本文关键字:引用 存储      更新时间:2023-10-16

我试图创建一个简单的模板enumerator类,它应该接受定义:操作符的任何对象,后来打印对形式(i, v[i])。一个简单的实现如下:

template<typename T>
struct enumerator {
    T &v; // reference to minimize copying
    enumerator(T &_v) : v(_v) {}
    void do_enumerate() {
        size_t i = 0;
        for(auto x : v) {
            cout << i << x << endl;
            i++;
        }
    }
};

情况下

vector<int> v({1,2,6,2,4});
auto e = enumerator(v);
e.do_enumerate();

然而,我也希望它处理临时对象,如:

案例B

auto e = enumerator(vector<int>({2,3,9});
e.do_enumerate();

这不起作用,编译器会抛出:

no matching function for call to ‘enumerator<std::vector<int> >::enumerator(std::vector<int>)

所以,我试着添加一个

enumerator(T _t) : t(_T) {}

构造函数来解决此错误。现在情况A不工作,有一个错误:

error: call of overloaded ‘enumerator(std::vector<int>&)’ is ambiguous

而且,在情形B中,枚举的输出是不正确的。

解决这个问题最干净的方法是什么?I would

  • 真的希望这两种情况都能正常工作
  • 建议不要使用stdc++以外的库
  • 希望尽可能少的复制(因此只是在结构体中存储T t不是一个选项)
  • c++ 11不是问题。我有c++ -4.8,我认为它已经完全支持c++ 11了。

如果参数是右值,我想复制,如果不是右值,就不复制。这可能吗?

这可以使用make_enumerator帮助函数完成,如下所示。

template <class T>
struct enumerator {
    T v;
    enumerator(T&& _v) : v(std::forward<T>(_v)) {}
    void do_enumerate() {
        size_t i = 0;
        for(auto x : v) {
            cout << i << x << endl;
            i++;
        }
    }
};
template <class T>
enumerator<T> make_enumerator(T&& x) {
    return enumerator<T>(std::forward<T>(x));
}
int main() {
    vector<int> v {5, 2, 9, 1};
    make_enumerator(v).do_enumerate();
    make_enumerator(std::move(v)).do_enumerate();
}

这是如何工作的?

如果make_enumerator的实参是A类型的左值,则T被演绎为A&,得到枚举数enumerator<A&>;如果A类型的右值,则T被演绎为A,得到枚举数enumerator<A>

在第一种情况下,成员enumerator::v将具有类型A&,这是绑定到构造函数实参的左值引用(没有复制)。在第二种情况下,成员的类型为A。使用std::forward将参数_v强制转换为右值,因此它将从初始化v时开始移动。

这是一个经典的例子,你实际上不需要class/struct(实际上引入了无用的代码),你可以使用好的旧函数:

template<typename Container>
void enumerate(const Container& t) {
    std::size_t i = 0;
    for(auto it = t.begin(); it != t.end(); ++it, ++i)
        std::cout << i << *it << std::endl;
}

,然后调用为:

enumerate(std::vector<int>{2,3,9});

现场演示

使用此方法,您还可以免费获得参数类型推断(这是struct无法获得的)。