C++ 带有参数列表初始化的模板参数推断

C++ 带有参数列表初始化的模板参数推断,c++,templates,c++17,template-argument-deduction,C++,Templates,C++17,Template Argument Deduction,我一直在尝试创建一个类,它表示数组的非拥有多维视图(有点像N维std::string\u视图),其中维度是“动态”变化的。即维度的数量和维度大小不与类绑定,而是在访问元素时指定(通过操作符())。以下代码总结了我正在寻找的功能: #include <array> #include <cstddef> template<typename T> struct array_view { T* _data; // Use of std::arra

我一直在尝试创建一个类,它表示数组的非拥有多维视图(有点像N维
std::string\u视图),其中维度是“动态”变化的。即维度的数量和维度大小不与类绑定,而是在访问元素时指定(通过
操作符()
)。以下代码总结了我正在寻找的功能:

#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;
}
我不是模板实例化/演绎方面的专家。然而,在我看来,编译器试图从
std::initializer\u list
参数中推断
N
,这失败了,因为
operator()
被声明为采用
std::array
参数。因此编译失败

这样做,显示出类似的结果:

template<typename T>
struct foo {
    T val;
};

struct bar {
    template<typename T>
    void operator()(foo<T>) {}
};

int main()
{
    bar b;
    b({1});
}
模板
结构foo{
T值;
};
结构条{
模板
void运算符()(foo){}
};
int main()
{
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});
main.cpp:在函数“int main()”中:
main.cpp:14:7:错误:调用'(bar)()时不匹配
b({1});
^
main.cpp:8:10:注意:候选者:'template void bar::operator()(foo)'
void运算符()(foo){}
^~~~~~~~
main.cpp:8:10:注意:模板参数扣除/替换失败:
main.cpp:14:7:注意:无法推断模板参数“t”
b({1});
编译器似乎甚至没有尝试将
{1}
(这是
foo
的有效初始化)转换为
foo
,因为它在推导函数模板参数失败后停止

那么,有什么方法可以实现我想要的功能吗?我是否缺少一些新的语法,或者有其他方法可以实现同样的功能,或者根本不可能实现

那么,有没有办法实现我想要的功能呢?是否有我遗漏的一些新语法,或者有一种替代方法可以做到这一点,或者根本不可能做到

显然,您可以按如下方式显式显示
N

view.operator()<3U>({{5U, 4U, 3U}}, {{0U, 0U, 0U}}) = 1;
view({{5U, 4U, 3U}}, 0U, 0U, 0U) = 1;
显然,在操作符内部使用
索引
,可能会更复杂,并且可能需要添加一些关于
Ts.
类型的检查(以验证所有索引都可转换为
std::size\u t

但是我想您也可以将
func()
方法定义为原始的
操作符()

下面是一个完整的工作示例

#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;
 }
#包括
#包括
模板
结构数组视图
{
T*_数据;
模板
T&func(标准::数组常量和dims,
std::数组常量和inds)常量
{
std::size_t offset=0/*计算简单偏移量*/;
返回_数据[偏移];
}
模板
T运算符()(标准::数组常量和dims,
Ts常数和…索引)常数
{return func(dims,{{index…}});}
};
int main()
{
int arr[3*4*5]={0};
数组_视图{arr};
视图({5U,4U,3U},0U,0U,0U)=1;
}

它无法编译的原因是
{5,4,3}
{0,0,0}
(称为带括号的init列表)与一级表达式不同。它们没有类型。在模板推导中,我们尝试将类型与表达式匹配,但我们无法针对大括号的init列表进行匹配。即使可以,大括号的init列表也不是任何类型的
std::array
,因此不匹配。这是我们需要额外语言支持的我们只是没有

有两个例外

最大的一个是
std::initializer\u list
。但是它具有我们在本例中不想要的运行时大小,因为您希望强制执行
维度
索引
参数具有相同的大小(大概)

另一个是原始数组。您可以从带括号的init列表推断出
T[N]
。因此您可以编写如下内容:

template <typename D, typename I, std::size_t N>
T& operator()(D const (&dimensions)[N], I const (&indices)[N]) const;
模板
T&运算符()(D常数和维数)[N],I常数和索引[N])常数;
这将允许您编写
视图({5,4,3},{0,0,0})
,它将
D
I
作为
int
N
作为
3
。它还将正确防止
视图({5,4,3},{0})
视图({5},{0,0})
编译


您可能需要添加额外的约束,使
D
I
是整数类型。

我喜欢数组的想法,因为它几乎与我想要的功能相同。但是,它迫使您使用相同的元素类型来调用它,这似乎是不必要的限制(使用我最初失败的方法,所有参数都将隐式转换为
std::size\t
)。我希望有一个
std::tuple
style
std::initializer\u list
,但没有。。。(甚至不能有编译时
std::initializer\u list
大小,即使标准规定底层数据存储为
t const[N]
。)
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... }}); }
#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;
 }
template <typename D, typename I, std::size_t N>
T& operator()(D const (&dimensions)[N], I const (&indices)[N]) const;