在编译时解析器中,我需要提取
std::string_value
文字的一部分并对其进行解码。
不幸的是没有编译,我所做的一个最小的例子是:
#include <charconv>
#include <iostream>
#include <optional>
#include <ranges>
#include <string_view>
template <typename X>
static constexpr X svtox(const std::string_view &sv)
{
X value;
auto result = std::from_chars(sv.data(), sv.data() + sv.size(), value);
return value;
}
static constexpr float parse(const std::string_view &sv)
{
const std::ranges::lazy_split_view tokens(sv, " ");
auto token_iterator = tokens.cbegin();
const auto keyword = *token_iterator++;
return svtox<float>(*token_iterator++);
}
int main()
{
const auto sv = std::string_view{"Whatever 1.0"};
float f = parse(sv);
std::cout << f;
}
可以肯定的是,真实的图案更复杂,提取的图案也更多,其中包含错误处理并进一步处理,而不仅仅是打印。
当我使用
c++ -std=gnu++2b -Wall -o test test.cpp
调用 GCC 13.2 时,出现以下错误:
test.cpp: In function ‘constexpr float parse(const std::string_view&)’:
test.cpp:20:25: error: invalid initialization of reference of type ‘const std::string_view&’ {aka ‘const std::basic_string_view<char>&’} from expression of type ‘std::basic_const_iterator<std::ranges::lazy_split_view<std::basic_string_view<char>, std::ranges::ref_view<const char [2]> >::_OuterIter<true> >::__reference’ {aka ‘std::__common_reference_impl<const std::ranges::lazy_split_view<std::basic_string_view<char>, std::ranges::ref_view<const char [2]> >::_OuterIter<true>::value_type&&, std::ranges::lazy_split_view<std::basic_string_view<char>, std::ranges::ref_view<const char [2]> >::_OuterIter<true>::value_type, 3, void>::type’}
20 | return svtox<float>(*token_iterator++);
| ^~~~~~~~~~~~~~~~~
test.cpp:8:50: note: in passing argument 1 of ‘constexpr X svtox(const std::string_view&) [with X = float; std::string_view = std::basic_string_view<char>]’
8 | static constexpr X svtox(const std::string_view &sv)
| ~~~~~~~~~~~~~~~~~~~~~~~~^~
我所处的环境可以使用以下
Dockerfile
(简化示例)重新创建:
FROM mcr.microsoft.com/devcontainers/cpp:1-ubuntu
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive && apt-get -y dist-upgrade && apt-get install -y ubuntu-release-upgrader-core && do-release-upgrade -p -f DistUpgradeViewNonInteractive -m server --allow-third-party && apt-get -y dist-upgrade && do-release-upgrade -d -f DistUpgradeViewNonInteractive -m server --allow-third-party && apt-get -y dist-upgrade
我对现代(对我来说是 C++98 之后的)C++ 相当陌生,所以我想我错过了一些简单的东西。
您遇到的问题是由于
std::degrees::lazy_split_view
返回的范围为 std::degrees::subrange items
,而不是 std::string_view
。
您可以通过使用 std::degrees::subrange
的 std::string_view
成员函数将 bottom()
转换为 std::degrees::subrange
来获取基础范围,然后从中构建 std::string_view
。
您可以修改您的 parse
函数来执行此操作:
static constexpr float parse(const std::string_view &sv)
{
const std::ranges::lazy_split_view tokens(sv, " ");
auto token_iterator = tokens.cbegin();
const auto keyword = *token_iterator++;
return svtox<float>(std::string_view{token_iterator->base().begin(), token_iterator->size()});
}
这将从
std::string_view
返回的 std::ranges::subrange
构造一个 lazy_split_view
,然后可以将其传递给 svtox
函数。请注意,这假设 subrange
的基础范围是连续的,std::string_view
就是这种情况。如果您要分割不同类型的范围,则需要确保它也是连续的。
由
lazy_split_view
分割的子范围最多仅模型 forward_range
,这就是为什么它被称为“惰性”。构造 string_view
需要 contiguous_range
。
相反,您可以使用
split_view
并将分割子范围显式转换为 string_view
:
auto tokens = sv | std::views::split(' ')
| std::views::transform([](auto r) { return std::string_view(r); });
auto token_iterator = tokens.cbegin();
请注意,与
lazy_split_view
相比,split_view
不是 const-iterable,因此您不能将其声明为 const 对象。