如何在编译时获取多维std :: vector的深度? [英] How can I get the depth of a multidimensional std::vector at compile time?

查看:115
本文介绍了如何在编译时获取多维std :: vector的深度?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个函数,该函数采用多维 std :: vector ,并且需要将深度(或维数)作为模板参数传递。而不是硬编码此值,我想编写一个 constexpr 函数,该函数将使用 std :: vector 并返回深度作为无符号整数值。

I have a function that takes a multidimensional std::vector and requires the depth (or the number of dimensions) to be passed in as a template parameter. Instead of hardcoding this value I would like to write a constexpr function that will take the std::vector and return the depth as an unsigned integer value.

例如:

std::vector<std::vector<std::vector<int>>> v =
{
    { { 0, 1}, { 2, 3 } },
    { { 4, 5}, { 6, 7 } },
};

// Returns 3
size_t depth = GetDepth(v);

此操作必须在编译时完成,因为此深度会传递给模板函数作为模板参数:

This needs to be done at compile time though because this depth will be passed to the template function as a template parameter:

// Same as calling foo<3>(v);
foo<GetDepth(v)>(v);

有没有办法做到这一点?

Is there any way to do this?

推荐答案

一个经典的模板问题。这是一个简单的解决方案,例如C ++标准库的工作方式。基本思想是要有一个递归模板,该递归模板将对每个维度一一计数,对于不是矢量的任何类型,其基本情况均为0。

A classic templating problem. Here's a simple solution like how the C++ standard library does. The basic idea is to have a recursive template that will count one by one each dimension, with a base case of 0 for any type that is not a vector.

#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)

所以您可以这样使用它:

So then you could use it like so:

dimensions<vector<vector<vector<int>>>>::value; // 3
// OR
dimensions_v<vector<vector<vector<int>>>>; // also 3 (C++17)

编辑:

好,我已经完成了所有容器类型的常规实现。请注意,根据表达式 begin(t),其中 std :: begin 导入用于ADL查找,而 t 是类型为 T 的左值。

Ok, I've finished the general implementation for any container type. Note that I defined a container type as anything that has a well-formed iterator type as per the expression begin(t) where std::begin is imported for ADL lookup and t is an lvalue of type T.

这是我的代码以及注释,以解释为什么东西有效以及我使用的测试用例。注意,这需要C ++ 17进行编译。

Here's my code along with comments to explain why stuff works and the test cases I used. Note, this requires C++17 to compile.

#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
}

这篇关于如何在编译时获取多维std :: vector的深度?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆