我需要一个泛型函数,它可以对容器进行常量或非常量引用,并返回对根据容器限定的元素的相应引用。
大致如下:
template <typename C>
auto get_nth( C& c, int i ) -> /* not-sure-what, but let's call it T */
{
//.... some tricky code here ...
}
我想强调的是,如果C扩展到
SomeContainer const
那么T就是
SomeContainer::const_reference
以及其他
SomeContainer::reference
我想我可以用类型特征和mtl把它放在一起,如果,我的问题是是否有一种更短、更干净的方法。
我使用的是C++x11(显然)和boost。
提前谢谢。
我认为您正在寻找typename C::reference
,请参阅23.2.1[container.requirements.general]§4。
哦,等等,如果C
已经是const
,那么上面的内容就不起作用了。但等一下,decltype
来救援!
template <typename C>
auto get_nth( C&& c, int i ) -> decltype(*c.begin())
{
//.... some tricky code here ...
}
如果您还想支持没有begin
成员函数的C样式数组:
#include <iterator>
template <typename C>
auto get_nth( C&& c, int i ) -> decltype(*std::begin(c))
{
//.... some tricky code here ...
}
而且实现真的不是那么棘手:
#include <iterator>
template <typename C>
auto get_nth( C&& c, int i ) -> decltype(*std::begin(c))
{
auto it = std::begin(c);
std::advance(it, i);
return *it;
}
注意,上面的解决方案接受左值和右值,但它将始终返回一个左值引用。根据客户端代码的不同,这可能是一个性能问题。以以下代码为例:
std::string s = get_nth(std::vector<std::string> { "hello", "world" }, 0);
这将把结果复制到s
中,即使移动它是完全有效的(当然,速度更快)。
为了解决这个问题,我们需要两个重载,一个用于左值,另一个用于右值:
#include <iterator>
#include <type_traits>
template <typename C>
auto get_nth( C& c, int i ) -> decltype(*std::begin(c))
{
auto it = std::begin(c);
std::advance(it, i);
return *it;
}
template <typename C>
auto get_nth( C&& c, int i )
-> typename std::enable_if<std::is_rvalue_reference<C&&>::value,
decltype(std::move(*std::begin(c)))>::type
{
auto it = std::begin(c);
std::advance(it, i);
return std::move(*it);
}
现在,结果将移动到s
中。enable_if
部分是必要的,因为由于引用折叠规则,C&&
也可以绑定到lvalues,然后初始化s
的调用将是不明确的。