使用 std::ranges::views::transform 应用转换构造函数的最小复制和粘贴方法是什么?

问题描述 投票:0回答:2

我正在开发一个

constexpr
解析器,它解析文本文件并生成对象表示。

我正在使用

G++ 14.1.0
,但也可以切换(现在我感觉 G++ 比
CLang
支持更多最新的 C++)。

我经常使用

std::ranges::views::split
,然后通过
std::string_view
将值转换为
std::ranges::views::split

最小示例代码:

#include <ranges>
#include <string_view>

using namespace std::literals::string_view_literals;

int main() {
    constexpr std::string_view input = "A,B B,C C,D C,E D,F"sv;
    constexpr auto working =
        input | std::ranges::views::split(","sv) |
        std::ranges::views::transform(
            [](const auto input) { return std::string_view(input); });
}

因为这是大量的复制和粘贴并使代码难以阅读,所以我尝试将转换构造函数的调用甚至整个转换移动到

constexpr
函数中。

我尝试的第一个是:

#include <ranges>
#include <string_view>

using namespace std::literals::string_view_literals;

int main() {
    constexpr std::string_view input = "A,B B,C C,D C,E D,F"sv;
    constexpr auto working =
        input | std::ranges::views::split(","sv) |
        std::ranges::views::transform(std::string_view::string_view);
}

,导致错误:

'string_view' is not a member of 'std::string_view' {aka 'std::basic_string_view<char>'}

和:

#include <ranges>
#include <string_view>

using namespace std::literals::string_view_literals;

int main() {
    constexpr std::string_view input = "A,B B,C C,D C,E D,F"sv;
    constexpr auto working =
        input | std::ranges::views::split(","sv) |
        std::ranges::views::transform(std::string_view);
}

,导致错误:

expected primary-expression before ')'

我尝试的第三个是:

#include <concepts>
#include <ranges>
#include <string_view>

using namespace std::literals::string_view_literals;

[[nodiscard]] static constexpr std::string_view to_string_view(const auto input)
    requires(std::constructible_from<std::string_view, decltype(input)>)
{
    return std::string_view(input);
}

int main() {
    constexpr std::string_view input = "A,B B,C C,D C,E D,F"sv;
    constexpr auto test = std::ranges::views::transform(
        input | std::ranges::views::split(","sv), to_string_view);
}

,这会导致错误

> <source>: In function 'int main()':
> <source>:15:56: error: no match for call to '(const std::ranges::views::_Transform) (std::ranges::split_view<std::basic_string_view<char>, std::basic_string_view<char> >, <unresolved overloaded function type>)'
> 15 |     constexpr auto test = std::ranges::views::transform(
>       |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
> 16 |         input | std::ranges::views::split(","sv), to_string_view);
>       |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> In file included from <source>:2:
> /opt/compiler-explorer/gcc-trunk-20240629/include/c++/15.0.0/ranges:2212:9: note: candidate: 'template<class _Range, class _Fp>  requires (viewable_range<_Range>) && (__can_transform_view<_Range, _Fp>) constexpr auto std::ranges::views::_Transform::operator()(_Range&&, _Fp&&) const'
>  2212 |         operator() [[nodiscard]] (_Range&& __r, _Fp&& __f) const
>       |         ^~~~~~~~
> /opt/compiler-explorer/gcc-trunk-20240629/include/c++/15.0.0/ranges:2212:9: note:   template argument deduction/substitution failed:
> <source>:15:56: note:   couldn't deduce template parameter '_Fp'
> 15 |     constexpr auto test = std::ranges::views::transform(
>       |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
> 16 |         input | std::ranges::views::split(","sv), to_string_view);
>       |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> /opt/compiler-explorer/gcc-trunk-20240629/include/c++/15.0.0/ranges:991:9: note: candidate: 'template<class ... _Args>  requires  __adaptor_partial_app_viable<_Derived, _Args ...> constexpr auto std::ranges::views::__adaptor::_RangeAdaptor<_Derived>::operator()(_Args&& ...) const [with _Args = {_Args ...}; _Derived = std::ranges::views::_Transform]'
> 991 |         operator()(_Args&&... __args) const
>       |         ^~~~~~~~
> /opt/compiler-explorer/gcc-trunk-20240629/include/c++/15.0.0/ranges:991:9: note:   template argument deduction/substitution failed:
> /opt/compiler-explorer/gcc-trunk-20240629/include/c++/15.0.0/ranges:991:9: note: constraints not satisfied
> /opt/compiler-explorer/gcc-trunk-20240629/include/c++/15.0.0/ranges: In substitution of 'template<class ... _Args>  requires  __adaptor_partial_app_viable<_Derived, _Args ...> constexpr auto std::ranges::views::__adaptor::_RangeAdaptor<std::ranges::views::_Transform>::operator()(_Args&& ...) const [with _Args = std::ranges::views::_Transform]':
> <source>:15:56:   required from here
> 15 |     constexpr auto test = std::ranges::views::transform(
>       |                           ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
> 16 |         input | std::ranges::views::split(","sv), to_string_view);
>       |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> /opt/compiler-explorer/gcc-trunk-20240629/include/c++/15.0.0/ranges:921:13:   required for the satisfaction of '__adaptor_partial_app_viable<_Derived, _Args ...>' [with _Derived = std::ranges::views::_Transform; _Args = {}]
> /opt/compiler-explorer/gcc-trunk-20240629/include/c++/15.0.0/ranges:922:28: note: the expression 'sizeof ... (_Args ...) == (_Adaptor::_S_arity) - 1 [with _Args = {}; _Adaptor = std::ranges::views::_Transform]' evaluated to 'false'
> 922 |       && (sizeof...(_Args) == _Adaptor::_S_arity - 1)
>       |          ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
> Compiler returned: 1

我猜这里的问题是,

to_string_view
没有为特定类型实例化,因此编译器找不到它,但这是一个猜测。

所以我尝试直接尝试最终目标,并将整个转换转移到一个函数中:

#include <concepts>
#include <functional>
#include <ranges>
#include <string_view>

using namespace std::literals::string_view_literals;

static auto constexpr to_string_transform_view = std::bind_back(
    std::ranges::views::transform, [](const auto input)
        requires(std::constructible_from<std::string_view, decltype(input)>)
    { return std::string_view(input); });

int main() {
    constexpr std::string_view input = "A,B B,C C,D C,E D,F"sv;
    auto constexpr test =
        to_string_transform_view(input | std::ranges::views::split(","sv));
}

需要帮助:可以编译,但我失去了很好的

|
语法,这更容易阅读。我怎样才能改进这一点,使 to_string_transform_view 的定义不会变得(太)难以阅读?

而且,好吧,现在只有一次,为了一件非常简单的事情而使用了这个“丑陋”的 lambda 函数。 因为我也需要这个来做其他事情,所以我更喜欢有更好的方法。

感谢 StackOverflow 中的各种答案,我看到

boost
提供了两个选项:

  1. boost::value_factory
#include <concepts>
#include <functional>
#include <ranges>
#include <string_view>

#include <boost/functional/value_factory.hpp>

using namespace std::literals::string_view_literals;

static auto constexpr to_string_transform_view = std::bind_back(
    std::ranges::views::transform, boost::value_factory<std::string_view>());

int main() {
    constexpr std::string_view input = "A,B B,C C,D C,E D,F"sv;
    auto constexpr test =
        to_string_transform_view(input | std::ranges::views::split(","sv));
}
  1. boost::lambda::constructor
#include <concepts>
#include <functional>
#include <ranges>
#include <string_view>

#include <boost/lambda/construct.hpp>

using namespace std::literals::string_view_literals;

static auto constexpr to_string_transform_view = std::bind_back(
    std::ranges::views::transform, boost::lambda::constructor<std::string_view>());

int main() {
    constexpr std::string_view input = "A,B B,C C,D C,E D,F"sv;
    auto constexpr test =
        to_string_transform_view(input | std::ranges::views::split(","sv));
}

问题:

  1. 同一件事只有两种实现吗?
  2. 在某些情况下,其中一种比另一种更适合吗?
  3. 有没有不需要第三方库的方法?
c++ c++23
2个回答
0
投票

不幸的是,

ranges::to
要求
C
必须not满足
view
,所以我们目前不能像

那样使用它
input | std::views::split(","sv)
      | std::views::transform(ranges::to<std::string_view>());

但是,可以制作一个在类型之间转换的函数对象来传递给

views::transform
,例如

template<class To>
inline constexpr auto static_caster = 
  []<class From>(From&& from) -> To
    requires requires { static_cast<To>(std::declval<From>()); }
  { return static_cast<To>(std::forward<From>(from)); };

constexpr std::string_view input = "A,B B,C C,D C,E D,F"sv;
/* constexpr */ auto working =
  input | std::views::split(","sv)
        | std::views::transform(static_caster<std::string_view>);

请注意,

split_view
不是
const
可迭代的,即它的
begin()
不是
const
限定的,因此它的
const
对象不是
range


0
投票

您不需要将

std::bind_back
应用于范围适配器对象。使用单个参数调用
std::ranges::views::transform
相当于使用
std::bind_back
,并且还支持
|
语法。

只需删除

bind_back
即可正常工作。

#include <concepts>
#include <ranges>
#include <string_view>

using namespace std::literals::string_view_literals;

constexpr auto to_string_transform_view =
    std::ranges::views::transform([](const auto input)
        requires(std::constructible_from<std::string_view, decltype(input)>)
    { return std::string_view(input); });

int main() {
    constexpr std::string_view input = "A,B B,C C,D C,E D,F"sv;
    constexpr auto test =
        input | std::ranges::views::split(","sv) | to_string_transform_view;
}

演示:https://godbolt.org/z/xc95b6hYf

© www.soinside.com 2019 - 2024. All rights reserved.