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

Sub*_*Das 5 c++ rvalue c++11

我正在尝试创建一个简单的模板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++;
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

这适用于以下事项:

案例A.

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

但是,我还希望它能够处理临时对象,例如:

案例B

auto e = enumerator(vector<int>({2,3,9});
e.do_enumerate();
Run Code Online (Sandbox Code Playgroud)

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

no matching function for call to ‘enumerator<std::vector<int> >::enumerator(std::vector<int>)
Run Code Online (Sandbox Code Playgroud)

所以,我试着添加一个

enumerator(T _t) : t(_T) {}
Run Code Online (Sandbox Code Playgroud)

构造函数来解决此错误.现在案例A不起作用,并且出现错误:

error: call of overloaded ‘enumerator(std::vector<int>&)’ is ambiguous
Run Code Online (Sandbox Code Playgroud)

而且,在情况B中,枚举的输出不正确.

解决这个问题的最简洁方法是什么?我会

  • 真的很喜欢这两种情况
  • 不想使用除stdc ++之外的任何库
  • 希望尽可能少的复制(因此只是T t在结构中存储一个不是一个选项)
  • C++ 11不是问题.我有g ++ - 4.8,我认为它有足够的C++ 11支持.

Bri*_*ian 6

好吧,如果参数是一个右值,我想复制,如果不是,则不复制.那可能吗?

这可以使用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();
}
Run Code Online (Sandbox Code Playgroud)

这是如何运作的?

如果参数to make_enumerator是一个类型的左值A然后T被推导为A&并且我们得到了枚举器enumerator<A&>,而如果它是一个类型的右值,A那么T推断为A,我们得到枚举器enumerator<A>.

在第一种情况下,成员enumerator::v将具有类型A&,左值引用绑定到构造函数参数(无复制).在第二种情况下,成员将具有类型A.使用std::forward将参数_v转换为rvalue,因此它将在用于初始化时移动v.