如何解决如何制作跨度跨度
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?
E/flutter ( 3477): [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: UnimplementedError: RecaptchaVerifier is not implemented
E/flutter ( 3477): #0 RecaptchaVerifierFactoryPlatform.instance (package:firebase_auth_platform_interface/src/platform_interface/platform_interface_recaptcha_verifier_factory.dart:
54:7)
E/flutter ( 3477): #1 RecaptchaVerifier._factory (package:firebase_auth/src/recaptcha_verifier.dart:11:40)
E/flutter ( 3477): #2 RecaptchaVerifier._factory (package:firebase_auth/src/recaptcha_verifier.dart)
E/flutter ( 3477): #3 new RecaptchaVerifier (package:firebase_auth/src/recaptcha_verifier.dart:56:7)
E/flutter ( 3477): #4 ApiProvider.signInWithPhoneNumber (package:clone_cgv/persistence/api_provider/api.dart:141:52)
E/flutter ( 3477): #5 Repository.signInWithPhoneNumber (package:clone_cgv/persistence/repositories/repo.dart:52:27)
E/flutter ( 3477): #6 LoginBloc.signInWithPhoneNumber (package:clone_cgv/blocs/login_blocs/bloc_login.dart:71:24)
E/flutter ( 3477): #7 _LoginPagesState._authenticateUserWithPhone (package:clone_cgv/pages/login_pages/login.dart:79:15)
E/flutter ( 3477): #8 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24)
E/flutter ( 3477): #9 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:607:11)
E/flutter ( 3477): #10 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:296:5)
E/flutter ( 3477): #11 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:267:7)
E/flutter ( 3477): #12 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:157:27)
E/flutter ( 3477): #13 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:385:20)
E/flutter ( 3477): #14 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:361:22)
E/flutter ( 3477): #15 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:278:11)
E/flutter ( 3477): #16 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:316:7)
E/flutter ( 3477): #17 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:280:5)
E/flutter ( 3477): #18 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:238:7)
E/flutter ( 3477): #19 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:221:7)
E/flutter ( 3477): #20 _rootRunUnary (dart:async/zone.dart:1370:13)
E/flutter ( 3477): #21 _CustomZone.runUnary (dart:async/zone.dart:1265:19)
E/flutter ( 3477): #22 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1170:7)
E/flutter ( 3477): #23 _invoke1 (dart:ui/hooks.dart:180:10)
E/flutter ( 3477): #24 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:276:7)
E/flutter ( 3477): #25 _dispatchPointerDataPacket (dart:ui/hooks.dart:96:31)
E/flutter ( 3477):
W/IInputConnectionWrapper( 3477): getTextBeforeCursor on inactive InputConnection
W/IInputConnectionWrapper( 3477): getSelectedText on inactive InputConnection
W/IInputConnectionWrapper( 3477): getTextAfterCursor on inactive InputConnection
E/zzf ( 3477): Problem retrieving SafetyNet Token: 7:
I/Timeline( 3477): Timeline: Activity_launch_request time:532100882
I/zzkn ( 3477): Provider GmsCore_OpenSSL not available
W/Activity( 3477): Slow Operation: Activity com.example.clone_cgv/com.google.firebase.auth.internal.RecaptchaActivity onResume took 123ms
W/System ( 3477): Ignoring header X-Firebase-Locale because its value was null.
I/Timeline( 3477): Timeline: Activity_launch_request time:532102862
W/System ( 3477): A resource failed to call end.
W/System ( 3477): Ignoring header X-Firebase-Locale because its value was null.
W/BpBinder( 3477): Slow Binder: BpBinder transact took 268ms,interface=com.google.android.gms.auth.api.phone.internal.ISmsRetrieverApiService,code=1 oneway=false
W/System ( 3477): Ignoring header X-Firebase-Locale because its value was null.
W/System ( 3477): Ignoring header X-Firebase-Locale because its value was null.
D/FirebaseAuth( 3477): Notifying id token listeners about user ( oPv63pimn1au0qx8tlinSS6L69xx
感谢 Barry 使用 standard ranges library 提出上述简化概念。
,只需使用模板打印包含 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),"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
的第 1 次使用很好,因为它是必需的,因为它可以是多种类型。
(注意:如果您确实需要在这里处理其他类型的字符,请忽略我在这里所说的并将 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),"Only strings of `std::wstring` or `std::wstring_view` are "
"allowed!");
for (wchar_t const ch : str) {
std::wcout << ch;
}
std::wcout << '\n';
}
}
您的目标似乎是能够使用您的自定义 print()
函数打印任何包含宽字符文本的容器类型,不是吗?
您似乎在称其为“矩阵”,其中容器中的外部元素是一个字符串,每个字符串的内部元素是一个宽字符(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
或 {{1} } 字符串类型被传入,并且 -
std::wstring_view
中的更多测试打印,包括main()
和std::vector<std::wstring>
的测试打印,以及链表:std::vector<std::wstring_view>
和无序集的测试打印(哈希集):std::list<std::wstring_view>
。
这里是完整的代码和 std::unordered_set<std::wstring>
函数模板。在线运行此代码:https://godbolt.org/z/TabW43Yjf。
print()
样本输出:
#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),"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
。
完整代码如下。在此处在线运行:https://godbolt.org/z/qjhqq647M。
std::wstring_view
为什么跨度跨度不起作用:
#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"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);
}
本质上只是一个包含指向连续内存块的指针的结构。 Cppreference.com states(强调):
类模板跨度描述了一个对象,该对象可以引用连续对象序列,其中序列的第一个元素位于零位置。
正如我在此处 (What is a "span" and when should I use one?) 的其他关于跨度的回答中所解释的那样,它可能如下所示:
std::span<T>
并非所有 C++ 容器类型都存储在连续内存中,例如链表(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
}
和 std::list
),因此不能将它们放入 span 中。
一般来说,span 是 C++ 中的一个包装器,用于包装 C 风格的数组,在一个变量中捕获指向其连续内存块的指针,在另一个变量中捕获它们的长度。这样,您可以用两个输入参数替换函数原型,如下所示:
std::forward_list
具有这样一个输入参数的原型:
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) {}
正如@mcilloni所说的in his comment here。
参考:
- 草稿:
- [我的回答] What is a "span" and when should I use one?
-
https://www.learncpp.com/cpp-tutorial/an-introduction-to-stdstring_view/ - 关于什么是
void do_stuff(std::span<T> data) {} // OR (the const form) void do_stuff(const std::span<T> data) {}
、何时以及为什么使用它以及如何使用它的优秀阅读。它还涵盖了它的一些细微差别、局限性和缺点。 - 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
- *****[我的回答--非常有用--我引用它是为了记住如何在编译时使用
std::string_view
] Use static_assert to check types passed to macro
更新:尽管因为看到它而被否决,我还是将答案留在下面,并且它下面的评论也有价值,但是 here's a different answer I just posted instead 我认为它具有价值和优点并且是正确的,应该被赞成. 我认为这个答案有足够的反对票来说明这一点。随意不要进一步贬低它。
我完全不理解在这里使用跨度的愿望(如果我遗漏了什么,请帮助我理解),如 the purpose of a span is to wrap and "C++-itize" (which is sometimes a debatable practice already) a C-style array。
为什么不直接改变这个:
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
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。