Ein klassisches Vorlagenproblem. Hier ist eine einfache Lösung wie die C ++ - Standardbibliothek. Die Grundidee besteht darin, eine rekursive Vorlage zu haben, die jede Dimension einzeln zählt, mit einem Basisfall von 0 für jeden Typ, der kein Vektor ist.
#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)
Dann könnten Sie es so verwenden:
dimensions<vector<vector<vector<int>>>>::value; // 3
// OR
dimensions_v<vector<vector<vector<int>>>>; // also 3 (C++17)
Bearbeiten:
Ok, ich habe die allgemeine Implementierung für jeden Containertyp abgeschlossen. Beachten Sie, dass ich einen Containertyp als alles definiert habe, das einen wohlgeformten Iteratortyp gemäß dem Ausdruck hat, begin(t)
in den std::begin
für die ADL-Suche importiert wird, und t
einen l-Wert vom Typ hat T
.
Hier ist mein Code zusammen mit Kommentaren, um zu erklären, warum Sachen funktionieren und welche Testfälle ich verwendet habe. Beachten Sie, dass zum Kompilieren C ++ 17 erforderlich ist.
#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
ist eine Laufzeitsache, keine zur Kompilierungszeit. Wenn Sie einen Container zur Kompilierungszeit benötigen, schauen Sie nachstd::array
. Ebenfalls; Beachten Sie, dassconstexpr
nur Mittel „ können bei der Kompilierung ausgewertet werden“ - es gibt kein Versprechen , dass es wird sein. Es kann zur Laufzeit ausgewertet werden.