我一直在尝试创建一个类,它表示一个非拥有的,多维的数组视图(有点像N维std::string_view
),其中维度是多变的"动态&#34 ;。即维度和维度大小的数量与类没有关联,但是在访问元素时指定(通过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;
}
但是这个fails to compile(忽略了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>
个参数。因此编译失败。
执行another, far more simplified, experiment,显示类似的结果:
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>
,因为它在未能推断出函数模板参数后停止。
那么有什么方法可以实现我正在寻找的功能吗?我是否缺少一些新的语法,或者采用相同的替代方法,或者是这根本不可能?
答案 0 :(得分:2)
那么有没有办法实现我正在寻找的功能?是否存在一些我缺少的新语法,或者是一种相同的替代方法,还是根本不可能?
显然,您可以将N
值显式如下
view.operator()<3U>({{5U, 4U, 3U}}, {{0U, 0U, 0U}}) = 1;
但我明白这是一个丑陋的解决方案。
对于另一种方法...如果你可以接受放弃第二个数组(并调用运算符,使用第二个初始化列表),并且你可以使用可变参数模板列表...大小可变列表的变量成为幸存数组的维度
template <typename ... Ts>
T & operator() (std::array<std::size_t, sizeof...(Ts)> const & dims,
Ts const & ... indices) const
{
std::size_t offset = 0 /* compute the simple offset */;
return _data[offset];
}
您可以按如下方式使用
view({{5U, 4U, 3U}}, 0U, 0U, 0U) = 1;
显然,在运算符中使用indices
可能会更复杂,并且可能需要添加一些关于Ts...
类型的检查(以验证它们都可以转换为std::size_t
但我想您也可以将func()
方法定义为原始operator()
template <std::size_t N>
T & func (std::array<std::size_t, N> const & dims,
std::array<std::size_t, N> const & inds) const
{
std::size_t offset = 0 /* compute the simple offset */;
return _data[offset];
}
您可以从operator()
template <typename ... Ts>
T & operator() (std::array<std::size_t, sizeof...(Ts)> const & dims,
Ts const & ... indices) const
{ return func(dims, {{ indices... }}); }
以下是一个完整的工作示例
#include <array>
#include <cstddef>
template <typename T>
struct array_view
{
T * _data;
template <std::size_t N>
T & func (std::array<std::size_t, N> const & dims,
std::array<std::size_t, N> const & inds) const
{
std::size_t offset = 0 /* compute the simple offset */;
return _data[offset];
}
template <typename ... Ts>
T & operator() (std::array<std::size_t, sizeof...(Ts)> const & dims,
Ts const & ... indices) const
{ return func(dims, {{ indices... }}); }
};
int main ()
{
int arr[3 * 4 * 5] = {0};
array_view<int> view{arr};
view({{5U, 4U, 3U}}, 0U, 0U, 0U) = 1;
}
答案 1 :(得分:1)
无法编译的原因是{0, 0, 0}
和std::array
(称为 braced-init-list )之类的东西不像第一类表达式。他们没有类型。在模板推导中,我们尝试将类型与表达式匹配 - 我们不能对 braced-init-list 执行此操作。即使我们可以, braced-init-list 也不是任何类型的std::initializer_list<T>
,因此不匹配。这是我们需要额外语言支持的东西,我们只是没有。
有两个例外。
最重要的是dimensions
。但是这具有我们在这种情况下不需要的运行时大小,因为您希望强制indices
和T[N]
参数具有相同的大小(可能)。
另一个是原始数组。您可以从braced-init-list中推导出template <typename D, typename I, std::size_t N>
T& operator()(D const (&dimensions)[N], I const (&indices)[N]) const;
。所以你可以这样写:
view({5, 4, 3}, {0, 0, 0})
这将允许您撰写D
,其中I
和int
将N
和3
推断为view({5, 4, 3}, {0})
。它还可以正确阻止编译view({5}, {0, 0})
和D
。
您可能希望添加I
和onPressItem = (id) => {
this.setState((state) => {
const selected = selected === id ? null : id;
return { selected };
});
};
为整数类型的其他约束。