可以使用具有可比较性但与分割范围值类型无关的分隔符吗?

huangapple go评论65阅读模式
英文:

Can i use split_view with a delimiter that's comparable, but otherwise not related to the split range's value type?

问题

std::ranges::split_view 通过将要拆分的范围与分隔符配对来工作。

然而,所述的分隔符以相当奇特的方式定义 - 它需要是一个 forward_range

幸运的是,标准允许使用 split_view,以便传递一个范围和一个单一元素。值得注意的是,这是标准中的一个示例:

string str{"the quick brown fox"};

for (auto word : views::split(str, ' ')) {
    cout << string_view(word) << '*';
}

如您所见,我们可以将 ' ' 作为分隔符传递。据我所知,这是通过使用以下构造函数实现的:

template<forward_range R>
requires constructible_from<V, views::all_t<R>> 
         && constructible_from<Pattern, single_view<range_value_t<R>>>
constexpr explicit split_view(R&& r, range_value_t<R> e);

这允许我们将 std::string 作为范围 R 传递,并将 char 作为分隔符 e 传递。requires 子句通过检查 char 是否是 std::stringrange_value_t(它是)以及是否可以创建一个 std::ranges::single_view<char> 来确保这将起作用(可能总是委托给假定分隔符是范围的实现)。效果非常好。

但如果我想要大幅度自定义我的拆分行为怎么办?例如,我想在任何空白字符上拆分。愚蠢的是,我以为这会起作用:

struct Whitespace {
    auto operator==(char const c) const noexcept -> bool {
        return std::isspace(static_cast<unsigned char>(c));
    }

    friend auto operator==(char const c, Whitespace const ws) noexcept -> bool {
        return ws == c;
    }
} whitespace;

auto main() -> int {
    auto const text = std::string("3213 421 43 3 532 5 53 53 5 3535 5353");

    namespace views = std::ranges::views;

    auto numbers = text | views::split(whitespace);
}

Whitespace 是既不是范围也不能从中创建 std::ranges::single_view<char> 的类型。即使它是,并且如果可以,也没有办法保留其自定义行为,因为上述构造函数将其转换为普通的 char

我是否可以以某种方式使用 std::views::split 以自定义逻辑来拆分,比如 char 范围?

英文:

std::ranges::split_view works by taking a range that is to be split paired with a delimiter.

However, said delimiter is defined in quite a peculiar way - it needs to be a forward_range.

Fortunately, the standard allows the usage of split_view such that a range and a single element is passed. Notably, this is an example from the standard:

string str{"the quick brown fox"};

for (auto word : views::split(str, ' ')) {
    cout << string_view(word) << '*';
}

As you can see, we can pass ' ' as a delimiter. To my knowledge, this works by employing the following constructor:

template<forward_range R>
requires constructible_from<V, views::all_t<R>> 
         && constructible_from<Pattern, single_view<range_value_t<R>>>
constexpr explicit split_view(R&& r, range_value_t<R> e);

This allows us to pass an std::string as the range R and a char as the delimiter e. The requires clause ensures that this will work by checking whether char is the range_value_t of an std::string (it is) and if it's possible to create an std::ranges::single_view<char> (potentially to always delegate to the implementation that assumes that the delimiter is a range). Works wonders.

But what if I wanted to heavily customize my splitting behavior? For example, I wanted to split on any whitespace. Foolishly, I thought that this will work:

struct Whitespace {
    auto operator==(char const c) const noexcept -> bool {
        return std::isspace(static_cast<unsigned char>(c));
    }

    friend auto operator==(char const c, Whitespace const ws) noexcept -> bool {
        return ws == c;
    }
} whitespace;

auto main() -> int {
    auto const text = std::string("3213 421 43 3 532 5 53 53 5 3535 5353");

    namespace views = std::ranges::views;

    auto numbers = text | views::split(whitespace);
}

But Whitespace is a type that is neither a range, not you can create an std::ranges::single_view<char> from it. And even if it was and if you could, there would be no way to retain its custom behaviors, given the fact that the mentioned constructor would convert it to plain, old char.

Can I somehow use std::views::split with a custom logic for splitting, let's say, ranges of chars?

答案1

得分: 0

No. split_view 需要 indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to>,而 ranges::equal_to 需要 equality_comparable_with(有一个不相关的小细节在这里不相关)。该概念的语义要求不允许非传递的“相等性”(即,t1 == ut2 == ut1 != t2),这对于匹配多个值的任何 u 都是必要的。

因此,所有尝试执行此类操作的都要么是 IFNDR,要么具有未定义的行为。

英文:

No. split_view requires indirectly_comparable&lt;iterator_t&lt;V&gt;, iterator_t&lt;Pattern&gt;, ranges::equal_to&gt;, and ranges::equal_to requires equality_comparable_with (with an minor caveat not relevant here). The semantic requirements of that concept disallows non-transitive "equality" (i.e., t1 == u, t2 == u but t1 != t2), which is necessary for any u that matches more than one value.

It follows that all attempts to do such things either are IFNDR or have undefined behavior.

huangapple
  • 本文由 发表于 2023年4月13日 22:41:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/76006774.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定