一个最小的例子,它显示了两种获取迭代器类型的方法,我会天真地期望它给出与结果相同的类型:
template <typename Range>
struct foo
{
using iterator = decltype(std::begin(std::declval<Range>()));
using iterator2 = typename Range::iterator;
static_assert(std::is_same<iterator, iterator2>::value, "Iterator types differ!");
};
int main()
{
std::vector<int> v;
foo<decltype(v)> f;
}
但这实际上触发了静态断言。
如果我们将第一个迭代器更改为:
using iterator = decltype(std::declval<Range>().begin());
没有触发静态断言。
查看std::begin()
的定义,对于引用和const引用类型都会重载。由于declval
给出了一个rvalue-reference,因此它只会绑定到一个const引用,从而返回一个const迭代器类型。
这可以使用参考折叠来解决一些令人厌恶的事情:
using iterator = decltype(std::begin(std::declval<typename std::add_lvalue_reference<Range>::type>()));
是否有更简单的方法来获取非const迭代器?显然typename Range::iterator
并不适用于所有类型(例如T*
),同样适用于成员begin()
,因此两者都不理想。
答案 0 :(得分:3)
直接调用std::begin
甚至不是获取开始迭代器的正确方法。正确的习语需要两个陈述:using std::begin; begin(rng);
。显然你无法在decltype
中做到。
您执行此操作的方式不适用于没有成员begin
/ end
或std::begin/end
超载的类型。< / p>
所以正确的解决方案是创建一个执行此操作的函数:
template<typename Range>
auto my_begin(Range &&rng)
{
using std::begin;
return begin(std::forward<Range>(rng));
}
然后在decltype
字段中调用该函数。