actions::transform fails on std::vector<T>, where T is a POD struct

72 Views Asked by At

This works just fine:

    std::vector<int> ints{10, 20, 30};
    auto floats = ints | ranges::to_vector
        | ranges::actions::transform([](auto n){ return 1 / float(n); });

Now, given the following struct:

struct Foo
{
    int* hello;
    int num;
    float yes;
};

The equivalent code:

    std::vector<Foo> foo;
    auto bar = foo | ranges::to_vector
        | ranges::actions::transform([](auto f){ return f.hello; });

will not compile. Digging through the error message to find the actual error (with concepts enabled), you'd find that the invocable_ concept is not satisfied, which usually means the transform function can't be applied to Foo.

Why doesn't this work? Foo is trivially copyable just like int, so it makes no sense that a generic lambda wouldn't be applicable here.

Godbolt link: https://godbolt.org/z/YWWYxn4bq

1

There are 1 best solutions below

1
On BEST ANSWER

It doesn't work because you are using ranges::actions::transform. Range-v3 has two transform functions: one is an action that transforms the entire input range, producing a new range of the same type; the other produces a view object that transforms each element lazily upon iterating.

The action works on the int vector because the transformation returns values that can be assigned to integers, but in the second example, int* cannot be assigned to Foo values.

Note that this also means that your first example produces a std::vector<int> containing three zeroes. To obtain a std::vector<float> instead, use the transform view and place it before to_vector:

std::vector<float> floats = ints 
    | ranges::views::transform([](auto n){ return 1 / float(n); })
    | ranges::to_vector;