实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
template<typename T>
class ReverseIterable {
public:
template<typename U>
explicit ReverseIterable(U&& u)
: iterable_(std::forward<U>(u))
{}

~ReverseIterable() = default;

ReverseIterable(const ReverseIterable&) = delete;

ReverseIterable& operator=(const ReverseIterable&) = delete;

auto begin() noexcept
{
return std::rbegin(iterable_);
}

auto end() noexcept
{
return std::rend(iterable_);
}

private:
T iterable_;
};

template<typename T>
auto MakeReverseIterable(T&& iterable)
{
// 1) if `iterable` is lvalue, then T is deduced as T&
// 2) if `iterable` is rvalue, then T is deduced as T
return ReverseIterable<T>(std::forward<T>(iterable));
}
  • 如果是左值,ReverseIterable 里存储的是这个值的引用,是否包含 const-qualifier 视参数而定
  • 如果是右值,ReverseIterable 里存值

上述类型切换刚好利用了 universal reference 的推导规则和 reference collapsing rules

手动去掉了 copy semantics,但是保留了 move semantics 因为我觉得会存在需要 move 的场景。

使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <set>
#include <vector>

template<typename T>
const T& as_const(const T& t) noexcept
{
return t;
}

int main()
{
std::vector v{ 1, 3, 5, 7, 9 };

std::cout << "--- lvalue ---\n";
for (const auto& ele : MakeReverseIterable(v)) {
std::cout << ele << " ";
}

std::cout << "\n--- lvalue as const ---\n";
for (const auto& ele : MakeReverseIterable(as_const(v))) {
std::cout << ele << " ";
}


std::cout << "\n--- rvalue ---\n";

auto gen = []() {
return std::set{ 3, 6, 2, 9, 7 };
};

for (const auto& ele : MakeReverseIterable(gen())) {
std::cout << ele << " ";
}

return 0;
}

然后就可以收工了。

一时手痒兴起之作。