Я пытался создать класс, который представляет не принадлежащий многомерный вид массива (вроде N-мерного std::string_view
), где размерность изменяется «динамически». Т.е. количество измерений и размерных размеров не привязано к классу, а указывается при доступе к элементам (через operator()
). Следующий код суммирует функциональность, которую я ищу:
#include <array>
#include <cstddef>
template<typename T>
struct array_view {
T* _data;
// Use of std::array here is not specific, I intend to use my own, but similar in functionality, indices class.
template<std::size_t N>
T& operator()(std::array<std::size_t, N> dimensions, std::array<std::size_t, N> indices) const
{
std::size_t offset = /* compute the simple offset */;
return _data[offset];
}
};
int main()
{
int arr[3 * 4 * 5] = {0};
array_view<int> view{arr};
/* Access element 0. */
// Should call array_view<int>::operator()<3>(std::array<std::size_t, 3>, std::array<std::size_t, 3>)
view({5, 4, 3}, {0, 0, 0}) = 1;
}
Однако это не может скомпилировать (игнорируя очевидную синтаксическую ошибку в operator()
) с
main.cpp: In function 'int main()':
main.cpp:28:27: error: no match for call to '(array_view<int>) (<brace-enclosed initializer list>, <brace-enclosed initializer list>)'
view({5, 4, 3}, {0, 0, 0}) = 1;
^
main.cpp:11:5: note: candidate: 'template<long unsigned int N> T& array_view<T>::operator()(std::array<long unsigned int, N>, std::array<long unsigned int, N>) const [with long unsigned int N = N; T = int]'
T& operator()(std::array<std::size_t, N> dimensions, std::array<std::size_t, N> indices) const
^~~~~~~~
main.cpp:11:5: note: template argument deduction/substitution failed:
main.cpp:28:27: note: couldn't deduce template parameter 'N'
view({5, 4, 3}, {0, 0, 0}) = 1;
^
Я не эксперт по конкретизации / выводу шаблонов. Однако мне кажется, что компилятор пытается вывести N
из std::initializer_list<int>
аргументов, что не удается, поскольку объявлено, что operator()
принимает std::array<std::size_t, N>
аргументы. Следовательно, компиляция не удалась.
Выполнение другого, гораздо более упрощенного эксперимента , показывает аналогичные результаты:
template<typename T>
struct foo {
T val;
};
struct bar {
template<typename T>
void operator()(foo<T>) {}
};
int main()
{
bar b;
b({1});
}
Выход:
main.cpp: In function 'int main()':
main.cpp:14:7: error: no match for call to '(bar) (<brace-enclosed initializer list>)'
b({1});
^
main.cpp:8:10: note: candidate: 'template<class T> void bar::operator()(foo<T>)'
void operator()(foo<T>) {}
^~~~~~~~
main.cpp:8:10: note: template argument deduction/substitution failed:
main.cpp:14:7: note: couldn't deduce template parameter 'T'
b({1});
Кажется, что компилятор даже не пытается преобразовать {1}
(который является действительной инициализацией foo<int>
) в foo<int>
, потому что он останавливается после неудачного вывода аргумента шаблона функции.
Так есть ли какой-нибудь способ добиться нужной мне функциональности? Есть ли какой-то новый синтаксис, который мне не хватает, или альтернативный подход, который делает то же самое, или это просто невозможно?