将“std::optional::value”传递给“std::views::transform”

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

提问人:Antonio 提问时间:8/24/2023 最后编辑:Jan SchultkeAntonio 更新时间:8/24/2023 访问量:84

问:

我似乎无法传递. 但是,我可以毫无问题地通过:std::optional::valuestd::views::transformstd::optional::has_value

#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; 
}

例如,这是我在 x86-64 clang 16.0.0 和传递标志时遇到的错误:-std=c++20

<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

你知道这是怎么回事吗?

自然地,作为一种解决方法,我可以使用 lambda,尽管这感觉并不那么令人满意:

const auto thisWorks = myVec
  | std::views::filter(&std::optional<int>::has_value)
  | std::views::transform([] (const auto& opt) {return opt.value();});
C++ C++20 范围 标准 可选

评论

2赞 StoryTeller - Unslander Monica 8/24/2023
C++ 标准库具有“可寻址”函数的概念。意思是程序可以合法地使用其地址的那些功能。这些成员未被指定为可寻址,因此无法保证。这是一个 lambda 或什么都没有(对两个成员)都承诺了。
2赞 Jarod42 8/24/2023
对于较大/不可复制的类型,lambda 应返回引用(带有尾随返回类型),而不是按值返回。

答:

3赞 Jan Schultke 8/24/2023 #1

使用 GCC 编译时,问题变得非常明显,从而产生错误:

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

特别注意<未解析的重载函数类型>

&std::optional<int>::value获取未解析的重载函数类型的地址,因为此成员函数具有 、non-、左值引用和右值引用的重载。 std::optional::value 总共有四个重载,编译器不知道在获取其地址时该选择哪一个。constconst

您可以按如下方式禁止显示该错误:

// 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);

请参阅编译器资源管理器中的实时示例

然而,虽然这在技术上是编译的,但它并不漂亮,也不是一个可寻址的功能。 这意味着获取其地址具有未指定的效果,程序甚至可能格式不正确。Смотритетакже: 我可以获取标准库中定义的函数的地址吗?value

您应该对 和 使用 lambda 的解决方法。这是惯用的和正确的解决方案,即使它不那么简洁。filtertransform

评论

0赞 Jarod42 8/24/2023
使用重载解析器,lambda 的详细程度相当。