c++ 就地转换string_view

omvjsjqw  于 2023-03-25  发布在  其他
关注(0)|答案(2)|浏览(152)

std::transform,从C++20开始,被声明为constexpr.我有一堆字符串实用函数,它们接受std::string参数,但很多用法最终只是传入小的,短的,字符文字序列。我想我应该利用这个事实,声明constexpr版本,并使用std::string_view s,而不是创建临时的std::string变量,只是为了扔掉它们。..
原始STD::STRING版本:

[[nodiscard]] std::string ToUpperCase(std::string string) noexcept {
    std::transform(string.begin(), string.end(), string.begin(), [](unsigned char c) -> unsigned char { return std::toupper(c, std::locale("")); });
    return string;
}

STD::STRING_VIEW版本:

[[nodiscard]] constexpr std::string_view ToUpperCase(std::string_view stringview) noexcept {
    std::transform(stringview.begin(), stringview.end(), stringview.begin(), [](unsigned char c) -> unsigned char { return std::toupper(c, std::locale("")); });
    return stringview;
}

但MSVC抱怨道:

error C3892: '_UDest': you cannot assign to a variable that is const

有没有一种方法可以用std::string_view调用std::transform,然后把它放回std::string_view,或者我必须创建一个本地字符串并返回它,从而破坏了最初使用std::string_view的目的?

[[nodiscard]] constexpr std::string ToUpperCase(std::string_view stringview) noexcept {
    std::string copy{stringview};
    std::transform(stringview.begin(), stringview.end(), copy.begin(), [](unsigned char c) -> unsigned char { return std::toupper(c, std::locale("")); });
    return copy;
}
hujrc8aj

hujrc8aj1#

不能就地转换std::string_view-如果它是从char *const**构造的呢?
很多用法最终只是在编译时传递小的、短的字符文字序列。
。。。但是可以将字符串字面量提升到类型级别

namespace impl {
    template<std::size_t n> struct Str {
        std::array<char, n> raw{};
        constexpr Str(char const (&src)[n + 1]) { std::copy_n(src, n, raw.begin()); }
    };
    template<std::size_t n> Str(char const (&)[n]) -> Str<n - 1>;
}
template<char... cs> struct Str { static char constexpr value[]{cs..., '\0'}; };
template<impl::Str s>
auto constexpr str_v = []<std::size_t... is>(std::index_sequence<is...>) {
    return Str<s.raw[is]...>{};
}(std::make_index_sequence<s.raw.size()>{});

...并添加一个特殊情况。一般来说,这种攻击可以通过范围/元组多态算法来避免。

[[nodiscard]] constexpr auto ToUpperCase(auto str) {
    for (auto&& c: str) c = ConstexprToUpper(c); // std::toupper doesn't seem constexpr
    return str;
}
template<char... cs> [[nodiscard]] constexpr auto ToUpperCase(Str<cs...>) {
    return Str<ConstexprToUpper(cs)...>{};
}

因此,要使用针对字符文字序列优化的转换,现在编写ToUpperCase(str_v<"abc">)而不是ToUpperCase("abc"sv)。如果您总是希望string_view作为输出,请在该重载中返回std::string_view{Str<ConstexprToUpper(cs)...>::value}

7d7tgy0s

7d7tgy0s2#

正如在一条评论中所说,span是一个更好的词汇表类型,因为单个元素可以被修改。我也不会将其设置为nodiscard,因为即使不分配结果,它也很有用:

#include <algorithm>
#include <cassert>
#include <cctype>
#include <locale>
#include <string_view>
#include <span>

constexpr std::span<char> ToUpperCase(std::span<char> stringview) noexcept {
    std::transform(stringview.begin(), stringview.end(), stringview.begin(),
        [](unsigned char c) -> unsigned char { return std::toupper(c); });
    return stringview;
}

int main() {
    std::string a = "compiler";
    std::string b = ToUpperCase(a);
    assert( a == "COMPILER");
    assert( b == "COMPILER");
}

https://godbolt.org/z/Toz8Y9bj9
有点偏离你最初的目标...我认为这是更优雅的,虽然受到膨胀和丑陋的编译错误。它在提供的情况下具有相同的效果。
我也不喜欢span的设计(或者string_view
(练习:添加概念)

template<class StringRange>
constexpr StringRange&& ToUpperCase(StringRange&& stringview) noexcept {
    std::transform(stringview.begin(), stringview.end(), stringview.begin(),
        [](unsigned char c) -> unsigned char { return std::toupper(c); });
    return std::forward<StringRange>(stringview);
}

https://godbolt.org/z/e9aWKMerE
我发现自己最近经常使用这个成语。

相关问题