microsoft / Range-V3-VS2015

A fork of the popular range-v3 C++ library with support for the Visual Studio 2015 Update 3 VC++ compiler.
Other
115 stars 22 forks source link

ranges::view::generate_n with std::reference_wrapper<std::random_device> #18

Open MathiasMagnus opened 7 years ago

MathiasMagnus commented 7 years ago

I wanted to tinker with ranges and MSVC but the simple demo I wanted to write for myself fails to compile. ranges::view::generate_n works fine with a lambda and does compile with a std::reference_wrapper<std::random_device>, but it asserts during the construction of the range.

AFAIK ref_wrapper should work with random_device, ref_wrapper preserves the functor nature (in the C++ sense) of whatever it is given. This code compiles and works:

using rng = std::random_device;
rng rand_dev;

std::vector<rng::result_type> ints;
std::generate_n(std::back_inserter(ints), 10, std::ref(rand_dev));

for (const auto& i : ints) std::cout << i << " ";
std::cout << std::endl;

So it would only make sense to expect the same kind of behavior when used in range-v3. However...

std::random_device rand_dev;

auto seeds = ranges::view::generate_n(std::ref(rand_dev), 8);

asserts inside range/v3/utility/variant.hpp:249 which is already the body of an active MSVC workaround macro region.

Am I doing something wrong?

CaseyCarter commented 7 years ago

Am I doing something wrong?

No, this should work, and strangely enough it seems to do so if you use the range-v3 reference wrapper instead of the version in the standard library. This program:

int main() {
    std::random_device rand_dev;
    auto seeds = ranges::view::generate_n(ranges::ref(rand_dev), 8);
    std::cout << seeds << '\n';
}

compiles and runs fine.

Hmm, the difference in reference_wrapper implementations is not the root cause, but it's related:

CONCEPT_ASSERT(!ranges::SemiRegular<std::reference_wrapper<std::random_device>>());
CONCEPT_ASSERT(ranges::SemiRegular<ranges::reference_wrapper<std::random_device>>());

ranges::reference_wrapper is Semiregular, so it gets stored directly in the generate_n_view. std::reference_wrapper is NOT Semiregular since it is not DefaultConstructible, so it gets stored inside an optional in the generate_n_view. range-v3 optional is implemented in terms of its variant, which is where I think the bug truly lies.

I'll look into this, but for now I suggest using ranges::ref(rand_dev) in place of std::ref(rand_dev) as a workaround.