如何制作跨度跨度

问题描述

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

godbolt.org

感谢 Barry 使用 standard ranges library 提出上述简化概念。

,

只需使用模板打印包含 std::wstringstd::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::wstringstd::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) {

...然后我补充说:

  1. a static_assert 依赖于 A) std::is_same_v<>(与 std::is_same<>::value 相同)和 B) decltype() 说明符以确保只有 std::wstring 或 {{1} } 字符串类型被传入,并且
  2. 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_assertstd::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

参考:

  1. 草稿:
    1. https://godbolt.org/z/s99dnzj8z
    2. https://godbolt.org/z/33vzTM787
  2. [我的回答] What is a "span" and when should I use one?
  3. 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) {} 、何时以及为什么使用它以及如何使用它的优秀阅读。它还涵盖了它的一些细微差别、局限性和缺点。
  4. https://en.cppreference.com/w/cpp/container/span
  5. https://en.cppreference.com/w/cpp/types/is_same
  6. https://en.cppreference.com/w/cpp/header/type_traits
  7. *****[我的回答--非常有用--我引用它是为了记住如何在编译时使用 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

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...