Một vấn đề templating cổ điển. Đây là một giải pháp đơn giản như cách thư viện chuẩn C ++ thực hiện. Ý tưởng cơ bản là có một mẫu đệ quy sẽ đếm từng cái một, với trường hợp cơ bản là 0 cho bất kỳ loại nào không phải là vectơ.
#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)
Vì vậy, sau đó bạn có thể sử dụng nó như vậy:
dimensions<vector<vector<vector<int>>>>::value; // 3
// OR
dimensions_v<vector<vector<vector<int>>>>; // also 3 (C++17)
Biên tập:
Ok, tôi đã hoàn thành việc triển khai chung cho bất kỳ loại container nào. Lưu ý rằng tôi đã xác định một loại container như bất cứ điều gì mà có một loại iterator tốt được hình thành theo các biểu hiện begin(t)
nơi std::begin
được nhập khẩu để tra cứu ADL và t
là một vế trái của loại T
.
Đây là mã của tôi cùng với các bình luận để giải thích lý do tại sao công cụ hoạt động và các trường hợp thử nghiệm tôi đã sử dụng. Lưu ý, điều này đòi hỏi C ++ 17 để biên dịch.
#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
là một thời gian chạy, không phải là một thời gian biên dịch. Nếu bạn muốn một thùng chứa kích thước thời gian biên dịch, hãy tìm đếnstd::array
. Cũng thế; hãy nhớ rằngconstexpr
chỉ có nghĩa là " có thể được đánh giá tại thời điểm biên dịch" - không có hứa hẹn rằng nó sẽ được. Nó có thể được đánh giá tại thời gian chạy.