c++ - 如何在编译时获得多维 std::vector 的深度?
问题描述
我有一个采用多维的函数,std::vector
需要将深度(或维数)作为模板参数传入。我不想对这个值进行硬编码,而是想编写一个constexpr
函数,它将std::vector
深度作为一个unsigned integer
值返回。
例如:
std::vector<std::vector<std::vector<int>>> v =
{
{ { 0, 1}, { 2, 3 } },
{ { 4, 5}, { 6, 7 } },
};
// Returns 3
size_t depth = GetDepth(v);
这需要在编译时完成,因为这个深度将作为模板参数传递给模板函数:
// Same as calling foo<3>(v);
foo<GetDepth(v)>(v);
有没有办法做到这一点?
解决方案
一个经典的模板问题。这是一个简单的解决方案,就像 C++ 标准库所做的那样。基本思想是有一个递归模板,每个维度都会一个一个地计数,对于任何不是向量的类型,基本情况都是 0。
#include <vector>
#include <type_traits>
template<typename T>
struct dimensions : std::integral_constant<std::size_t, 0> {};
template<typename T>
struct dimensions<std::vector<T>> : std::integral_constant<std::size_t, 1 + dimensions<T>::value> {};
template<typename T>
inline constexpr std::size_t dimensions_v = dimensions<T>::value; // (C++17)
那么你可以像这样使用它:
dimensions<vector<vector<vector<int>>>>::value; // 3
// OR
dimensions_v<vector<vector<vector<int>>>>; // also 3 (C++17)
编辑:
好的,我已经完成了任何容器类型的一般实现。请注意,我将容器类型定义为具有格式正确的迭代器类型的任何内容,根据为 ADL 查找导入的表达式begin(t)
,它是type 的左值。std::begin
t
T
这是我的代码和注释,用于解释为什么这些东西有效以及我使用的测试用例。注意,这需要 C++17 编译。
#include <iostream>
#include <vector>
#include <array>
#include <type_traits>
using std::begin; // import std::begin for handling C-style array with the same ADL idiom as the other types
// decide whether T is a container type - i define this as anything that has a well formed begin iterator type.
// we return true/false to determing if T is a container type.
// we use the type conversion ability of nullptr to std::nullptr_t or void* (prefers std::nullptr_t overload if it exists).
// use SFINAE to conditionally enable the std::nullptr_t overload.
// these types might not have a default constructor, so return a pointer to it.
// base case returns void* which we decay to void to represent not a container.
template<typename T>
void *_iter_elem(void*) { return nullptr; }
template<typename T>
typename std::iterator_traits<decltype(begin(*(T*)nullptr))>::value_type *_iter_elem(std::nullptr_t) { return nullptr; }
// this is just a convenience wrapper to make the above user friendly
template<typename T>
struct container_stuff
{
typedef std::remove_pointer_t<decltype(_iter_elem<T>(nullptr))> elem_t; // the element type if T is a container, otherwise void
static inline constexpr bool is_container = !std::is_same_v<elem_t, void>; // true iff T is a container
};
// and our old dimension counting logic (now uses std:nullptr_t SFINAE logic)
template<typename T>
constexpr std::size_t _dimensions(void*) { return 0; }
template<typename T, std::enable_if_t<container_stuff<T>::is_container, int> = 0>
constexpr std::size_t _dimensions(std::nullptr_t) { return 1 + _dimensions<typename container_stuff<T>::elem_t>(nullptr); }
// and our nice little alias
template<typename T>
inline constexpr std::size_t dimensions_v = _dimensions<T>(nullptr);
int main()
{
std::cout << container_stuff<int>::is_container << '\n'; // false
std::cout << container_stuff<int[6]>::is_container<< '\n'; // true
std::cout << container_stuff<std::vector<int>>::is_container << '\n'; // true
std::cout << container_stuff<std::array<int, 3>>::is_container << '\n'; // true
std::cout << dimensions_v<std::vector<std::array<std::vector<int>, 2>>>; // 3
}
推荐阅读
- python - Cartopy 使用正交投影错误地绘制点
- webview - 是否可以在混合应用程序中使用 NativeScript 的 WebView 作为 WebMap?
- node.js - 自动确认用户 Cognito + Node JS
- python - 当我尝试列出所有文件名时获取特殊字符
- javascript - 如何在没有过渡和内联样式添加到 html 的情况下创建幻灯片?可能吗?
- spring-webflux - 反应式编程 - 在完成时调用另一个 Mono #SpringWebFlux
- mongodb - MongoDB聚合和数组内公共字段的总和
- odoo - 在特定时间触发 Odoo 的预定操作(例如每天凌晨 1 点到 6 点)
- android - 在 kotlin 中加载图片 url 不显示图片
- apache - 从某些位置找不到网站 404