如何制作跨度的跨度
How to make span of spans
C++20 std::span
是一个非常好的编程接口。但是似乎没有一种简单的方法可以跨度。这是我正在尝试做的事情:
#include <iostream>
#include <span>
#include <string>
#include <vector>
void print(std::span<std::span<wchar_t>> matrix) {
for (auto const& str : matrix) {
for (auto const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
int main() {
std::vector<std::wstring> vec = {L"Cool", L"Cool", L"Cool"};
print(vec);
}
这无法编译。我该怎么做?
为什么不使用 concept 呢?
#include <iostream>
#include <string>
#include <vector>
#include <ranges>
template <class R, class T>
concept Matrix =
std::convertible_to<
std::ranges::range_reference_t<std::ranges::range_reference_t<R>>,
T>;
void print(Matrix<wchar_t> auto const& matrix) {
for (auto const& str : matrix) {
for (auto const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
int main() {
std::vector<std::wstring> vec = {L"Cool", L"Cool", L"Cool"};
print(vec);
}
感谢Barry for suggesting the simplified concept above using the standard ranges library。
更新:尽管自从看到它和它下面的评论也有价值以来,尽管有反对票,但我还是在下面留下了答案,但是 我认为它具有价值和优点。
我完全不明白在这里使用跨度的愿望(如果我遗漏了什么,请帮助我理解),因为 。
为什么不改变这个:
void print(std::span<std::span<wchar_t>> matrix) {
到这个?:
void print(std::vector<std::wstring> matrix) {
现在代码工作正常 (run on Godbolt):
#include <iostream>
// #include <span> // not needed
#include <string>
#include <vector>
void print(std::vector<std::wstring> matrix) {
for (auto const& str : matrix) {
for (auto const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
int main() {
std::vector<std::wstring> vec = {L"Cool", L"Cool", L"Cool"};
print(vec);
}
这是输出,如 Godbolt 上所示。请注意,文本 (Cool Cool Cool
) 打印得很好:
ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
Cool
Cool
Cool
只需使用模板打印包含 std::wstring
或 std::wstring_view
的任何容器类型(两个任意类型限制用于演示;根据需要轻松调整或删除这些限制)
我更喜欢坚持使用更具普遍可读性的代码(C++“概念”非常先进,但没有得到广泛理解)。为什么不直接使用这个简单的模板?
template <typename T>
void print(const T& matrix) {
for (auto const& str : matrix) {
for (auto const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
作为奖励,添加此 static_assert
以检查类型并确保仅传入 std::wstring
或 std::wstring_view
字符串类型,例如(修改或删除静态断言如您所愿,并根据您的需要):
static_assert(std::is_same_v<decltype(str), const std::wstring&> ||
std::is_same_v<decltype(str), const std::wstring_view&>,
"Only strings of `std::wstring` or `std::wstring_view` are "
"allowed!");
现在你有这个更好版本的print()
函数模板:
template <typename T>
void print(const T& matrix) {
for (auto const& str : matrix) {
static_assert(std::is_same_v<decltype(str), const std::wstring&> ||
std::is_same_v<decltype(str), const std::wstring_view&>,
"Only strings of `std::wstring` or `std::wstring_view` are "
"allowed!");
for (auto const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
但是,auto
的第二次使用不是必需的,也没有增加任何价值(它只会混淆事物),所以让我们删除它,并改用它:
for (wchar_t const ch : str) {
auto
的第一种用法很好,因为它是必需的,因为它可以是多种类型。
(注意:如果你确实需要在这里处理其他类型的字符,请忽略我在这里所说的并将 wchar_t
改回 auto
。就这样了由您决定。)
现在,我们有了 最终 版本的 printf()
函数模板:
template <typename T>
void print(const T& matrix) {
for (auto const& str : matrix) {
static_assert(std::is_same_v<decltype(str), const std::wstring&> ||
std::is_same_v<decltype(str), const std::wstring_view&>,
"Only strings of `std::wstring` or `std::wstring_view` are "
"allowed!");
for (wchar_t const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
看来您的目标是能够使用自定义 print()
函数打印 any 包含宽字符文本的容器类型,不是吗?
你好像把它叫做“矩阵”,容器中的外部元素是一个字符串,每个字符串的内部元素是一个宽字符(wchar)。
如果是这样的话,下面的模板就可以了。我只是改变了这个:
void print(std::span<std::span<wchar_t>> matrix) {
对此:
template <typename T>
void print(const T& matrix) {
...然后我补充说:
- a
static_assert
依靠 A) std::is_same_v<>
(与 std::is_same<>::value
相同)和 B) decltype()
说明符以确保仅 std::wstring
或 std::wstring_view
字符串类型传入,并且
main()
中的更多测试打印,包括 std::vector<std::wstring>
和 std::vector<std::wstring_view>
的测试打印,以及链表:std::list<std::wstring_view>
和无序集(哈希集):std::unordered_set<std::wstring>
.
这是完整的代码和print()
函数模板。 运行在线代码:https://godbolt.org/z/TabW43Yjf.
#include <iostream>
#include <list> // added for demo purposes to print a linked list in main()
// #include <span> // not needed
#include <string>
#include <type_traits> // added to check types and aid with static asserts
#include <unordered_set>
#include <vector>
template <typename T>
void print(const T& matrix) {
for (auto const& str : matrix) {
static_assert(std::is_same_v<decltype(str), const std::wstring&> ||
std::is_same_v<decltype(str), const std::wstring_view&>,
"Only strings of `std::wstring` or `std::wstring_view` are "
"allowed!");
for (wchar_t const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
int main() {
std::vector<std::wstring> vec1 = {L"Cool1", L"Cool2", L"Cool3"};
std::vector<std::wstring_view> vec2 = {L"Hey1", L"Hey2", L"Hey3"};
std::list<std::wstring_view> list1 = {L"You1", L"You2", L"You3"};
std::unordered_set<std::wstring> set1 = {L"There1", L"There2", L"There3"};
print(vec1);
print(vec2);
print(list1);
print(set1);
// Compile-time error due to the std::is_same_v<> usage in the static_assert
// above!
// std::vector<std::string> vec3 = {"hey", "you"};
// print(vec3);
}
示例输出:
Cool1
Cool2
Cool3
Hey1
Hey2
Hey3
You1
You2
You3
There3
There2
There1
如果你只想打印 std::vector<std::wstring>
和 std::vector<std::wstring_view>
类型,这里有一个更受限制的模板(同样,为了演示,这是两个任意类型限制;轻松调整或删除您认为合适的这些限制):
只需在我上面的模板中替换它:
template <typename T>
void print(const T& matrix) {
这样,强制它只接受std::vector<>
容器类型(上面的const T&
改为下面的const std::vector<T>&
,就是全部):
template <typename T>
void print(const std::vector<T>& matrix) {
然后,根据需要添加 static_assert
以确保向量中的类型是 std::wstring
或 std::wstring_view
。
完整代码如下。 运行 在这里在线:https://godbolt.org/z/qjhqq647M.
#include <iostream>
// #include <span> // not needed
#include <string>
#include <type_traits>
#include <vector>
template <typename T>
void print(const std::vector<T>& matrix) {
static_assert(std::is_same_v<T, std::wstring> ||
std::is_same_v<T, std::wstring_view>,
"Only vectors of `std::wstring` or `std::wstring_view` are allowed!");
for (auto const& str : matrix) {
for (wchar_t const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
int main() {
std::vector<std::wstring> vec1 = {L"Cool1", L"Cool2", L"Cool3"};
std::vector<std::wstring_view> vec2 = {L"Hey1", L"Hey2", L"Hey3"};
print(vec1);
print(vec2);
// Compile-time error due to the std::is_same_v<> usage in the static_assert
// above!
// std::vector<std::string> vec3 = {"hey", "you"};
// print(vec3);
}
为什么 span of span 不起作用:
std::span<T>
本质上只是一个包含指向 连续内存块 的指针的结构。 Cppreference.com states(强调):
The class template span describes an object that can refer to a contiguous sequence of objects with the first element of the sequence at position zero.
正如我在此处关于 span 的其他回答中所解释的那样 (),它可能看起来像这样:
template <typename T>
struct span
{
T * ptr_to_array; // pointer to a contiguous C-style array of data
// (which memory is NOT allocated or deallocated
// by the span)
std::size_t length; // number of elements in the array
// Plus a bunch of constructors and convenience accessor methods here
}
但是,并非所有 C++ 容器类型都存储在连续内存中,例如链表(std::list
和 std::forward_list
),因此它们不能放在一个跨度中。
一般来说,span 是 C++ 中的包装器,用于包装 C 风格的数组,在一个变量中捕获指向其连续内存块的指针,在另一个变量中捕获它们的长度。这样,您可以用两个输入参数替换函数原型,如下所示:
void do_stuff(T *ptr_to_data, std::size_t num_elements) {}
// OR (the const form)
void do_stuff(const T *ptr_to_data, std::size_t num_elements) {}
带有一个像这样的输入参数的原型:
void do_stuff(std::span<T> data) {}
// OR (the const form)
void do_stuff(const std::span<T> data) {}
作为@mcilloni says .
参考文献:
- 草稿:
- [我的回答]
- https://www.learncpp.com/cpp-tutorial/an-introduction-to-stdstring_view/ - 关于什么是
std::string_view
、何时使用、为何使用以及如何使用的精彩阅读。它还涵盖了它的一些细微差别、限制和缺点。
- https://en.cppreference.com/w/cpp/container/span
- https://en.cppreference.com/w/cpp/types/is_same
- https://en.cppreference.com/w/cpp/header/type_traits
- *****[我的回答——非常有用——我引用它是为了记住如何在编译时使用
static_assert(std::is_same_v<decltype(var), some_type>, "some msg");
] Use static_assert to check types passed to macro 静态检查类型
C++20 std::span
是一个非常好的编程接口。但是似乎没有一种简单的方法可以跨度。这是我正在尝试做的事情:
#include <iostream>
#include <span>
#include <string>
#include <vector>
void print(std::span<std::span<wchar_t>> matrix) {
for (auto const& str : matrix) {
for (auto const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
int main() {
std::vector<std::wstring> vec = {L"Cool", L"Cool", L"Cool"};
print(vec);
}
这无法编译。我该怎么做?
为什么不使用 concept 呢?
#include <iostream>
#include <string>
#include <vector>
#include <ranges>
template <class R, class T>
concept Matrix =
std::convertible_to<
std::ranges::range_reference_t<std::ranges::range_reference_t<R>>,
T>;
void print(Matrix<wchar_t> auto const& matrix) {
for (auto const& str : matrix) {
for (auto const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
int main() {
std::vector<std::wstring> vec = {L"Cool", L"Cool", L"Cool"};
print(vec);
}
感谢Barry for suggesting the simplified concept above using the standard ranges library。
更新:尽管自从看到它和它下面的评论也有价值以来,尽管有反对票,但我还是在下面留下了答案,但是
我完全不明白在这里使用跨度的愿望(如果我遗漏了什么,请帮助我理解),因为
为什么不改变这个:
void print(std::span<std::span<wchar_t>> matrix) {
到这个?:
void print(std::vector<std::wstring> matrix) {
现在代码工作正常 (run on Godbolt):
#include <iostream>
// #include <span> // not needed
#include <string>
#include <vector>
void print(std::vector<std::wstring> matrix) {
for (auto const& str : matrix) {
for (auto const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
int main() {
std::vector<std::wstring> vec = {L"Cool", L"Cool", L"Cool"};
print(vec);
}
这是输出,如 Godbolt 上所示。请注意,文本 (Cool Cool Cool
) 打印得很好:
ASM generation compiler returned: 0 Execution build compiler returned: 0 Program returned: 0 Cool Cool Cool
只需使用模板打印包含 std::wstring
或 std::wstring_view
的任何容器类型(两个任意类型限制用于演示;根据需要轻松调整或删除这些限制)
我更喜欢坚持使用更具普遍可读性的代码(C++“概念”非常先进,但没有得到广泛理解)。为什么不直接使用这个简单的模板?
template <typename T>
void print(const T& matrix) {
for (auto const& str : matrix) {
for (auto const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
作为奖励,添加此 static_assert
以检查类型并确保仅传入 std::wstring
或 std::wstring_view
字符串类型,例如(修改或删除静态断言如您所愿,并根据您的需要):
static_assert(std::is_same_v<decltype(str), const std::wstring&> ||
std::is_same_v<decltype(str), const std::wstring_view&>,
"Only strings of `std::wstring` or `std::wstring_view` are "
"allowed!");
现在你有这个更好版本的print()
函数模板:
template <typename T>
void print(const T& matrix) {
for (auto const& str : matrix) {
static_assert(std::is_same_v<decltype(str), const std::wstring&> ||
std::is_same_v<decltype(str), const std::wstring_view&>,
"Only strings of `std::wstring` or `std::wstring_view` are "
"allowed!");
for (auto const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
但是,auto
的第二次使用不是必需的,也没有增加任何价值(它只会混淆事物),所以让我们删除它,并改用它:
for (wchar_t const ch : str) {
auto
的第一种用法很好,因为它是必需的,因为它可以是多种类型。
(注意:如果你确实需要在这里处理其他类型的字符,请忽略我在这里所说的并将 wchar_t
改回 auto
。就这样了由您决定。)
现在,我们有了 最终 版本的 printf()
函数模板:
template <typename T>
void print(const T& matrix) {
for (auto const& str : matrix) {
static_assert(std::is_same_v<decltype(str), const std::wstring&> ||
std::is_same_v<decltype(str), const std::wstring_view&>,
"Only strings of `std::wstring` or `std::wstring_view` are "
"allowed!");
for (wchar_t const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
看来您的目标是能够使用自定义 print()
函数打印 any 包含宽字符文本的容器类型,不是吗?
你好像把它叫做“矩阵”,容器中的外部元素是一个字符串,每个字符串的内部元素是一个宽字符(wchar)。
如果是这样的话,下面的模板就可以了。我只是改变了这个:
void print(std::span<std::span<wchar_t>> matrix) {
对此:
template <typename T>
void print(const T& matrix) {
...然后我补充说:
- a
static_assert
依靠 A)std::is_same_v<>
(与std::is_same<>::value
相同)和 B)decltype()
说明符以确保仅std::wstring
或std::wstring_view
字符串类型传入,并且 main()
中的更多测试打印,包括std::vector<std::wstring>
和std::vector<std::wstring_view>
的测试打印,以及链表:std::list<std::wstring_view>
和无序集(哈希集):std::unordered_set<std::wstring>
.
这是完整的代码和print()
函数模板。 运行在线代码:https://godbolt.org/z/TabW43Yjf.
#include <iostream>
#include <list> // added for demo purposes to print a linked list in main()
// #include <span> // not needed
#include <string>
#include <type_traits> // added to check types and aid with static asserts
#include <unordered_set>
#include <vector>
template <typename T>
void print(const T& matrix) {
for (auto const& str : matrix) {
static_assert(std::is_same_v<decltype(str), const std::wstring&> ||
std::is_same_v<decltype(str), const std::wstring_view&>,
"Only strings of `std::wstring` or `std::wstring_view` are "
"allowed!");
for (wchar_t const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
int main() {
std::vector<std::wstring> vec1 = {L"Cool1", L"Cool2", L"Cool3"};
std::vector<std::wstring_view> vec2 = {L"Hey1", L"Hey2", L"Hey3"};
std::list<std::wstring_view> list1 = {L"You1", L"You2", L"You3"};
std::unordered_set<std::wstring> set1 = {L"There1", L"There2", L"There3"};
print(vec1);
print(vec2);
print(list1);
print(set1);
// Compile-time error due to the std::is_same_v<> usage in the static_assert
// above!
// std::vector<std::string> vec3 = {"hey", "you"};
// print(vec3);
}
示例输出:
Cool1 Cool2 Cool3 Hey1 Hey2 Hey3 You1 You2 You3 There3 There2 There1
如果你只想打印 std::vector<std::wstring>
和 std::vector<std::wstring_view>
类型,这里有一个更受限制的模板(同样,为了演示,这是两个任意类型限制;轻松调整或删除您认为合适的这些限制):
只需在我上面的模板中替换它:
template <typename T>
void print(const T& matrix) {
这样,强制它只接受std::vector<>
容器类型(上面的const T&
改为下面的const std::vector<T>&
,就是全部):
template <typename T>
void print(const std::vector<T>& matrix) {
然后,根据需要添加 static_assert
以确保向量中的类型是 std::wstring
或 std::wstring_view
。
完整代码如下。 运行 在这里在线:https://godbolt.org/z/qjhqq647M.
#include <iostream>
// #include <span> // not needed
#include <string>
#include <type_traits>
#include <vector>
template <typename T>
void print(const std::vector<T>& matrix) {
static_assert(std::is_same_v<T, std::wstring> ||
std::is_same_v<T, std::wstring_view>,
"Only vectors of `std::wstring` or `std::wstring_view` are allowed!");
for (auto const& str : matrix) {
for (wchar_t const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
int main() {
std::vector<std::wstring> vec1 = {L"Cool1", L"Cool2", L"Cool3"};
std::vector<std::wstring_view> vec2 = {L"Hey1", L"Hey2", L"Hey3"};
print(vec1);
print(vec2);
// Compile-time error due to the std::is_same_v<> usage in the static_assert
// above!
// std::vector<std::string> vec3 = {"hey", "you"};
// print(vec3);
}
为什么 span of span 不起作用:
std::span<T>
本质上只是一个包含指向 连续内存块 的指针的结构。 Cppreference.com states(强调):
The class template span describes an object that can refer to a contiguous sequence of objects with the first element of the sequence at position zero.
正如我在此处关于 span 的其他回答中所解释的那样 (
template <typename T>
struct span
{
T * ptr_to_array; // pointer to a contiguous C-style array of data
// (which memory is NOT allocated or deallocated
// by the span)
std::size_t length; // number of elements in the array
// Plus a bunch of constructors and convenience accessor methods here
}
但是,并非所有 C++ 容器类型都存储在连续内存中,例如链表(std::list
和 std::forward_list
),因此它们不能放在一个跨度中。
一般来说,span 是 C++ 中的包装器,用于包装 C 风格的数组,在一个变量中捕获指向其连续内存块的指针,在另一个变量中捕获它们的长度。这样,您可以用两个输入参数替换函数原型,如下所示:
void do_stuff(T *ptr_to_data, std::size_t num_elements) {}
// OR (the const form)
void do_stuff(const T *ptr_to_data, std::size_t num_elements) {}
带有一个像这样的输入参数的原型:
void do_stuff(std::span<T> data) {}
// OR (the const form)
void do_stuff(const std::span<T> data) {}
作为@mcilloni says
参考文献:
- 草稿:
- [我的回答]
- https://www.learncpp.com/cpp-tutorial/an-introduction-to-stdstring_view/ - 关于什么是
std::string_view
、何时使用、为何使用以及如何使用的精彩阅读。它还涵盖了它的一些细微差别、限制和缺点。 - https://en.cppreference.com/w/cpp/container/span
- https://en.cppreference.com/w/cpp/types/is_same
- https://en.cppreference.com/w/cpp/header/type_traits
- *****[我的回答——非常有用——我引用它是为了记住如何在编译时使用
static_assert(std::is_same_v<decltype(var), some_type>, "some msg");
] Use static_assert to check types passed to macro 静态检查类型