c++ 如何制作跨中跨

o4hqfura  于 2022-11-27  发布在  其他
关注(0)|答案(4)|浏览(135)

C++20 std::span是一个非常好的编程接口。但是似乎没有一个简单的方法来拥有一个span的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);
}

这不能编译,我怎么做这样的事?

xlpyo6sf

xlpyo6sf1#

为什么不使用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);
}

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

ccgok5k5

ccgok5k52#

在C++23中,标准增加了<mdspan>头文件来解决这个问题。正如你可能猜到的那样,多维span的名称是标准。然而,它的用法相当复杂。这种复杂性是对STL对抽象完整性承诺的补充。我会推荐你参考一些记录它的用法和建议的源文件。
参考文献:

如果你打算在发行版上升级到C++23,我建议你同时使用参考实现。如果没有,你也可以写你自己的实现。写你自己的实现会相当复杂,因为它有如此广泛的API。
将来在cppreference上将提供更多的宽容的文档;目前尚未完成。

jqjz2hbq

jqjz2hbq3#

只需使用一个模板来打印任何包含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字符串类型,例如(根据需要修改或删除静态Assert):

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()函数打印 * 任何 * 包含宽字符文本的容器类型,是吗?

您似乎将其称为“矩阵”,其中容器中的外部元素是字符串,每个字符串的内部元素是宽字符(wchar)。
如果是这样的话,下面的模板就可以工作了。我只是简单地修改了一下:

void print(std::span<std::span<wchar_t>> matrix) {

更改为:

template <typename T>
void print(const T& matrix) {

......然后我补充道:

  1. static_assert依赖于A)std::is_same_v<>(与std::is_same<>::value相同)和B)decltype()说明符以确保仅传入std::wstringstd::wstring_view字符串类型,以及
  2. 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::wstringstd::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"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);
}

多个跨度不起作用的原因:

std::span<T>本质上只是一个包含指向 * 连续内存块 * 的指针的结构体。Cppreference.com states(着重号是添加的):
类模板span描述了一个对象,该对象可以引用一个连续的对象序列,该序列的第一个元素位于位置零
正如我在这里关于span的另一个答案中所解释的(什么是“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::liststd::forward_list),因此它们不能放置在span中。
一般来说,span是C
中的一个 Package 器,用于 Package 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在这里的评论中所说。

参考文献:

1.划痕:

  1. https://godbolt.org/z/s99dnzj8z
  2. https://godbolt.org/z/33vzTM787
    1.[我的回答]什么是“span”,什么时候应该使用?
  3. https://www.learncpp.com/cpp-tutorial/an-introduction-to-stdstring_view/-关于什么是std::string_view,何时和为什么使用它,以及如何使用的优秀阅读。它还涵盖了它的一些细微差别,限制和缺点。
  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
    1.*****[我的答案--非常有用--我引用它是为了记住如何在编译时使用static_assert(std::is_same_v<decltype(var), some_type>, "some msg");静态检查类型]使用static_assert检查传递给宏的类型
vd8tlhqk

vd8tlhqk4#

我完全不理解在这里使用span的愿望(如果我遗漏了什么,请帮助我理解),如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

相关问题