Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Pass `std::optional::value` to `std::views::transform`

I cannot seem to pass std::optional::value to std::views::transform.
However, I can pass std::optional::has_value without any issues:

#include <optional>
#include <ranges>
#include <vector>

int main () {

    std::vector<std::optional<int>> myVec{{1,2,3}};

    const auto result = myVec
        | std::views::filter(&std::optional<int>::has_value)
        | std::views::transform(&std::optional<int>::value);

    return 0; 
}

As an example, here is the error I get with x86-64 clang 16.0.0 and passing the -std=c++20 flag:

<source>:15:11: error: no matching function for call to object of type 'const _Transform'
        | std::views::transform(&std::optional<int>::value);
          ^~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/ranges:891:2: note: candidate template ignored: substitution failure: deduced incomplete pack <(no value)> for template parameter '_Args'
        operator()(_Args&&... __args) const
        ^
/opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/ranges:2057:2: note: candidate function template not viable: requires 2 arguments, but 1 was provided
        operator() [[nodiscard]] (_Range&& __r, _Fp&& __f) const
        ^
1 error generated.
Compiler returned: 1

Do you know what is going on?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Natually, as a workaround, I can use a lambda, though that doesn’t feel nearly as satisfactory:

const auto thisWorks = myVec
  | std::views::filter(&std::optional<int>::has_value)
  | std::views::transform([] (const auto& opt) {return opt.value();});

>Solution :

The problem becomes very clear when compiling with GCC, which yields the error:

error: no match for call to '(const std::ranges::views::_Transform) (<unresolved overloaded function type>)'
   15 |         | std::views::transform(&std::optional<int>::value);
      |           ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~

Pay special attention to <unresolved overloaded function type>.

&std::optional<int>::value takes the address of an unresolved overloaded function type because this member function has overloads for const, non-const, lvalue references, and rvalue references.
In total, std::optional::value has four overloads, and the compiler doesn’t know which one to choose when you take its address.

You can suppress the error as follows:

// This works because we are manually resolving the type through an implicit
// conversion, similar to how overload resolution works when calling functions.
int& (std::optional<int>::*value_fun)() & = &std::optional<int>::value;

const auto result = myVec
    | std::views::filter(&std::optional<int>::has_value)
    | std::views::transform(value_fun);

See live example at Compiler Explorer.

However, while this technically compiles, it’s not pretty, and value is not an addressable function.
This means that taking its address has unspecified effect, and the program might even be ill-formed. See also: Can I take the address of a function defined in standard library?

You should use your workaround with lambdas for both filter and transform. It is the idiomatic and correct solution, even if it isn’t as concise.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading