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

时间:2014-06-12 01:45:12

标签: c++ c++11 rvalue

我正在尝试创建一个简单的模板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++;
        }
    }
};

这适用于以下内容:

案例A

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中,枚举的输出不正确。

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

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

2 个答案:

答案 0 :(得分: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();
}

这是如何运作的?

如果make_enumerator的参数是A类型的左值,则T推断为A&,我们得到枚举器enumerator<A&>,如果它是A类型的右值,然后T推导为A,我们得到了枚举器enumerator<A>

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

答案 1 :(得分:3)

这是一个经典示例,您实际上并不需要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});

Live demo

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