我正在尝试创建一个简单的模板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中,枚举的输出不正确。
解决这个问题的最简洁方法是什么?我会
T t
不是一个选项)答案 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});
使用此方法,您还可以免费获得参数类型推断(使用struct
无法获得)。