C++ 如何制作跨中跨

C++ 如何制作跨中跨,c++,c++20,std-span,C++,C++20,Std Span,C++20STD::span是一个非常好的编程接口。但似乎没有一种简单的方法可以跨越一个又一个跨度。以下是我正在尝试做的: #include <iostream> #include <span> #include <string> #include <vector> void print(std::span<std::span<wchar_t>> matrix) { for (auto const& str :

C++20STD::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);
}
这不能编译。我该如何做这样的事情呢?

为什么不改用a呢

包括 包括 包括 包括 样板 概念矩阵= 标准::可转换为< std::ranges::range\u reference\t, T> ); void printMatrix自动常量和矩阵{ 对于自动常量&str:matrix{ 对于自动常量ch:str{ std::wcout为什么不改用a

包括 包括 包括 包括 样板 概念矩阵= 标准::可转换为< std::ranges::range\u reference\t, T> ); void printMatrix自动常量和矩阵{ 对于自动常量&str:matrix{ 对于自动常量ch:str{ std::wcout更新:尽管有反对票,我还是留下了下面的答案,因为我看到了它,下面的评论也有价值,但我认为它有价值和优点,是正确的,应该被提升。我认为这个答案有足够的反对票来说明这一点。请不要再往下投了

我完全不理解在这里使用span的愿望,如果我遗漏了什么,请帮助我理解,例如

为什么不改变这一点:

void print(std::span<std::span<wchar_t>> matrix) {
更新:我留下下面的答案,尽管有反对票,因为看到它和下面的评论也有价值,但我认为它有价值和优点,是正确的,应该被提升。我认为这个答案有足够的反对票来说明这一点。请不要再往下投了

我完全不理解在这里使用span的愿望,如果我遗漏了什么,请帮助我理解,例如

为什么不改变这一点:

void print(std::span<std::span<wchar_t>> matrix) {
只需使用模板打印包含std::wstring或std::wstring的任何容器类型,即可查看两个任意类型限制,以便于演示;根据需要轻松调整或删除这些限制 我更喜欢坚持代码,它是更普遍的可读性C++概念非常先进,并没有被广泛理解。为什么不只是使用这个简单的模板?< /P>
template <typename T>
void print(const T& matrix) {
  for (auto const& str : matrix) {
    for (auto const ch : str) {
      std::wcout << ch;
    }
    std::wcout << '\n';
  }
}
现在您有了打印功能模板的更好版本:

auto的第一次使用很好,因为它是必需的,因为它可以是多种类型

注意:如果您确实需要在这里处理其他类型的字符,请忽略我在这里所说的内容,并将wchar\u t更改回auto。这取决于您的决定

现在,我们有了printf函数模板的最终版本:

为此:

template <typename T>
void print(const T& matrix) {
样本输出:

如果您只想打印std::vector和std::vector类型,这里还有一个更有限的模板,为了便于演示,这是两个任意类型的限制;您可以根据需要轻松调整或删除这些限制: 只需在上面的我的模板中替换此内容:

template <typename T>
void print(const T& matrix) {
然后,根据需要,添加一个静态_断言以确保向量中的类型是std::wstring或std::wstring_视图

下面是完整代码。请在此处联机运行:

使用带有一个输入参数的原型,如下所示:

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
}
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) {}
正如我所说的

参考资料: 临时工作: [我的回答] -阅读什么是std::string_视图、何时使用、为什么使用以及如何使用,非常棒。本书还介绍了它的一些细微差别、局限性和缺点。 *****[我的答案非常有用——我引用它是为了记住如何在编译时使用static_assertstd::is_same_v,some msg;]静态检查类型] 只需使用模板打印包含std::wstring或std::wstring的任何容器类型,即可查看两个任意类型限制,以便于演示;根据需要轻松调整或删除这些限制 我更喜欢坚持代码,它是更普遍的可读性C++概念非常先进,并没有被广泛理解。为什么不只是使用这个简单的模板?< /P>
template <typename T>
void print(const T& matrix) {
  for (auto const& str : matrix) {
    for (auto const ch : str) {
      std::wcout << ch;
    }
    std::wcout << '\n';
  }
}
现在您有了打印功能模板的更好版本:

auto的第一次使用很好,因为它是必需的,因为它可以是多种类型

注意:如果您确实需要在这里处理其他类型的字符,请忽略我在这里所说的内容,并将wchar\u t更改回auto。这取决于您的决定

现在,我们有了printf函数模板的最终版本:

为此:

template <typename T>
void print(const T& matrix) {
样本输出:

如果您只想打印std::vector和std::vector类型,这里还有一个更有限的模板,为了便于演示,这是两个任意类型的限制;您可以根据需要轻松调整或删除这些限制: 只需在上面的我的模板中替换此内容:

template <typename T>
void print(const T& matrix) {
然后,根据需要,添加一个静态_断言以确保向量中的类型是std::wstring或std::wstring_视图

下面是完整代码。请在此处联机运行:

使用带有一个输入参数的原型,如下所示:

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
}
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) {}
正如我所说的

参考资料: 临时工作: [我的回答] -阅读什么是std::string_视图、何时使用、为什么使用以及如何使用它非常好。它还涵盖了它的一些细微差别、限制和缺点 gs。 *****[我的答案非常有用——我引用它是为了记住如何在编译时使用static_assertstd::is_same_v,some msg;]静态检查类型]

编译错误是什么?为什么不是一个span向量?您希望引用哪个std::span对象数组?听起来你想要一个跨度范围,但外部的跨度可能不是一个跨度。我认为跨度范围没有任何意义。跨度的数组或向量可以,但不是跨度的跨度。关于跨度是什么,请参见我的答案,或者这里的另一个答案:@Ay:实际上,错误是没有从向量到跨度的转换。这完全不奇怪。编译错误是什么?为什么不是一个span向量?您希望引用哪个std::span对象数组?听起来你想要一个跨度范围,但外部的跨度可能不是一个跨度。我认为跨度范围没有任何意义。跨度的数组或向量可以,但不是跨度的跨度。关于跨度是什么,请参见我的答案,或者这里的另一个答案:@Ay:实际上,错误是没有从向量到跨度的转换。这完全不奇怪。该函数应该能够处理wstring向量和wstring_视图向量。在这方面,你的回答如何公平?@AyxanHaqverdili,我没有意识到这是问题的局限性,因为你的问题表明没有任何地方。我得走了。std::span的要点是,您可以获取一个T的容器,而不必关心它是向量、列表、deque、std::数组、原始数组还是其他什么。它基本上是STL的2迭代器习惯用法,但打包方式更好。我认为这是显而易见的。@AyxanHaqverdili不完全正确,std::span需要引用连续的内存区域,因此它仅限于C数组、std::array、std::vector,就是这样-它基本上是一个元组,包含指向T的指针和一个可选的包含元素数的std::size\U T值。它的主要目的是对传递指针及其大小的常见用法进行抽象,用void do_stuff std::span tees替换void do_stuff t*ptr,std::size_t count。@AyxanHaqverdili:它只能处理连续的容器,因此std::deque不存在,as是通过转换容器的每个元素形成的范围,除非您实际为结果形成另一个连续容器。该函数应能够处理wstring向量和wstring_视图向量。在这方面,你的回答如何公平?@AyxanHaqverdili,我没有意识到这是问题的局限性,因为你的问题表明没有任何地方。我得走了。std::span的要点是,您可以获取一个T的容器,而不必关心它是向量、列表、deque、std::数组、原始数组还是其他什么。它基本上是STL的2迭代器习惯用法,但打包方式更好。我认为这是显而易见的。@AyxanHaqverdili不完全正确,std::span需要引用连续的内存区域,因此它仅限于C数组、std::array、std::vector,就是这样-它基本上是一个元组,包含指向T的指针和一个可选的包含元素数的std::size\U T值。它的主要目的是对传递指针及其大小的常见用法进行抽象,用void do_stuff std::span tees替换void do_stuff t*ptr,std::size_t count。@AyxanHaqverdili:它只能处理连续的容器,因此std::deque不存在,as是通过转换容器的每个元素而形成的范围,除非您为结果实际形成另一个连续容器。该概念不检查任何内容。或者更确切地说,它要么是真的,要么是不正确的。。。这并不是你想要的概念:@Barry谢谢你指出这一点,在你发现后,我花了一段时间才想出一些令我满意的东西。上面的答案。只需使用std::ranges::range。然后range_reference_t是一种类型特征,它可以获得range的引用类型。不需要你自己滚。你可以依赖于范围,而不是已经需要的范围,所以简而言之:这个概念不检查任何东西。或者更确切地说,它要么是真的,要么是不正确的。。。这并不是你想要的概念:@Barry谢谢你指出这一点,在你发现后,我花了一段时间才想出一些令我满意的东西。上面的答案。只需使用std::ranges::range。然后range_reference_t是一种类型特征,它可以获得range的引用类型。不需要你自己滚。你可以依赖于已经需要范围的范围参考,所以简而言之:@PatrickRoberts,很好地捕捉到我的输入参数中缺少常数&要打印。这肯定是我的疏忽。我已经修好了。我还讨论了关于合理诊断消息的另一个问题,通过使用静态断言检查传递给模板的输入类型,并在用户使用时输出良好的诊断消息
错了,谢谢你的努力!您的解决方案的问题在于它不是一个可重用的概念。您必须将这些检查分散在代码库中,并保持同步。除非您深入并读取实现中的所有断言,否则也无法立即清楚输入类型是什么。最后,它仅限于std::wstring和std::wstring_视图,不会处理包含wchar__的任何其他类型。概念答案可以很容易地转换为使用任何类型的矩阵,只需很少的努力。@AyxanHaqverdili,您的解决方案的问题在于它不是一个可重用的概念。您必须将这些检查分散在代码库中,并保持同步。嗯,那不完全是真的。你根本不需要支票!但是,是的,如果你想要支票,你必须保持同步。除非您深入并读取实现中的所有断言,否则也无法立即清楚输入类型是什么。正确,使用该函数的doxygen标头轻松解决。但是,我看C++概念,我一点儿也不知道它在做什么或者什么类型。最后,AyxanHaqverdili仅限于std::wstring和std::wstring_视图&不会处理包含wchar_的任何其他类型。这是我强行施加的任意限制,因为这是你在我被否决的答案下的评论中所说的两种类型。要使其处理更多类型,只需删除静态断言或向其检查中添加新类型。概念答案可以很容易地转换为使用任何类型的矩阵,只需付出最小的努力。我的答案也是如此。我根据你以前的评论任意限制输入类型,试图猜测你想要什么。@加布里埃斯塔普斯:在C++中使用的功能越少,在合理的范围内,我认为越好。这也是一个很好的方法来保持一个人的知识缺乏这些特点。你声称你想要C++来简单化,但是概念特征是为了给你提供你渴望的简单性。这意味着普通程序员可以轻松地使用复杂的SFINAE技术。我们为您提供了使语言更简单的功能,但您不会使用它们。我不知道你想从语言中得到什么。@PatrickRoberts,很好地了解到我要打印的输入参数中缺少常量。这肯定是我的疏忽。我已经修好了。我还解决了关于合理诊断消息的另一个问题,通过使用静态断言检查传递给模板的输入类型,并在用户使用错误类型时输出良好的诊断消息。我感谢您的努力!您的解决方案的问题在于它不是一个可重用的概念。您必须将这些检查分散在代码库中,并保持同步。除非您深入并读取实现中的所有断言,否则也无法立即清楚输入类型是什么。最后,它仅限于std::wstring和std::wstring_视图,不会处理包含wchar__的任何其他类型。概念答案可以很容易地转换为使用任何类型的矩阵,只需很少的努力。@AyxanHaqverdili,您的解决方案的问题在于它不是一个可重用的概念。您必须将这些检查分散在代码库中,并保持同步。嗯,那不完全是真的。你根本不需要支票!但是,是的,如果你想要支票,你必须保持同步。除非您深入并读取实现中的所有断言,否则也无法立即清楚输入类型是什么。正确,使用该函数的doxygen标头轻松解决。但是,我看C++概念,我一点儿也不知道它在做什么或者什么类型。最后,AyxanHaqverdili仅限于std::wstring和std::wstring_视图&不会处理包含wchar_的任何其他类型。这是我强行施加的任意限制,因为这是你在我被否决的答案下的评论中所说的两种类型。要使其处理更多类型,只需删除静态断言或向其检查中添加新类型。概念答案可以很容易地转换为使用任何类型的矩阵,只需付出最小的努力。我的答案也是如此。我根据你以前的评论任意限制输入类型,试图猜测你想要什么。@加布里埃斯塔普斯:在C++中使用的功能越少,在合理的范围内,我认为越好。这也是一个很好的方法来保持一个人的知识缺乏这些特点。你声称你想要C++来简单化,但是概念特征是为了给你提供你渴望的简单性。这意味着普通程序员可以轻松地使用复杂的SFINAE技术。我们为您提供了使语言更简单的功能,但您不会使用它们。我不知道你想从语言中得到什么。
template <typename T>
void print(const std::vector<T>& matrix) {
#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);
}
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
}
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) {}