How to store a range as a field in a class?

2019-08-27 03:59发布

问题:

I would like to store a range as a field in a class, so that I could reuse it several times later on. However, unlike local variables, I cannot simply specify it type as auto. On the other hand, the types of ranges that the library creates are very complex. It would take me disproportionately long time to figure out the correct type by hand + it would be unmaintainable in the future if I would choose to change how the range is obtained.

So, I thought, maybe I could use decltype to help myself:

class MyClass {
    public:
    using MyRange = decltype(std::declval<std::vector<int*>>() | ranges::v3::view::filter([=](int* elem) { return true; }));
    MyRange range;
}

(note: my actual std::declval is actually more complex, but I wanted to make the example brief.)

But I get an error: a lambda cannot appear in an unevaluated context

So, my question is:

  • How can I avoid using the lambda and get my decltype working?
  • Or maybe there is a better/cleaner way to get a type of a range in order to declare it as a field in a class?

回答1:

The language is being helpful here: if the lambda were allowed in decltype, it wouldn't help you, because there would be no way for you to produce a value of type MyRange other than via default-initialization. Since very occurrence of a lambda expression has a unique type, you couldn't produce a function object of the correct type to pass to view::filter to get it to return something you could store in a MyRange.

The workaround is to "don't do that"; e.g., store your function object somewhere and refer to it in the decltype. In C++17, for example:

struct MyClass {
    static constexpr auto pred = [](int* elem) { return true; };
    using MyRange = decltype(std::declval<std::vector<int*>&>() | ranges::v3::view::filter(pred));
    MyRange range;
};

Note the use of std::vector&. This is required as explained in Explicit range-v3 decltype evaluates to void?



标签: c++ range-v3