[英]Passing variadic parameters in an already template-variadic function
The title is bad but I couldn't come up with anything better. 标题不好,但我无法提出更好的建议。 Feel free to change it.
随时更改它。
Here's a template multidimensional array class that I'm currently working on. 这是我当前正在处理的模板多维数组类。 I'm trying to optimise it as much as I can:
我正在尝试尽可能优化它:
#include <array>
template <typename T, std::size_t... Dimensions>
class multidimensional_array
{
public:
using value_type = T;
using size_type = std::size_t;
private:
template<typename = void>
static constexpr size_type multiply(void)
{
return 1u;
}
template<std::size_t First, std::size_t... Other>
static constexpr size_type multiply(void)
{
return First * multidimensional_array::multiply<Other...>();
}
public:
using container_type = std::array<value_type, multidimensional_array::multiply<Dimensions...>()>;
using reference = value_type &;
using const_reference = value_type const&;
using iterator = typename container_type::iterator;
private:
container_type m_data_array;
template<typename = void>
static constexpr size_type linearise(void)
{
return 0u;
}
template<std::size_t First, std::size_t... Other>
static constexpr size_type linearise(std::size_t index, std::size_t indexes...)
{
return multidimensional_array::multiply<Other...>()*index + multidimensional_array::linearise<Other...>(indexes);
}
public:
// Constructor
explicit multidimensional_array(const_reference value = value_type {})
{
multidimensional_array::fill(value);
}
// Accessors
reference operator()(std::size_t indexes...)
{
return m_data_array[multidimensional_array::linearise<Dimensions...>(indexes)];
}
const_reference operator()(std::size_t indexes...) const
{
return m_data_array[multidimensional_array::linearise<Dimensions...>(indexes)];
}
// Iterators
iterator begin()
{
return m_data_array.begin();
}
iterator end()
{
return m_data_array.end();
}
// Other
void fill(const_reference value)
{
m_data_array.fill(value);
}
};
My main function is 我的主要功能是
int main(void)
{
multidimensional_array<int, 2u, 3u, 4u, 5u, 6u> foo;
int k = 0;
for (auto& s : foo)
s = k++;
//std::cout << foo(0u, 0u, 0u, 1u, 0u) << std::endl;
return 0;
}
The above code compilers without warning/error. 上面的代码编译器没有警告/错误。 As soon as I uncomment the
std::cout
part though, I get this: 一旦我取消注释
std::cout
部分,我就会得到:
g++-7 -std=c++17 -o foo.o -c foo.cpp -Wall -Wextra -pedantic
foo.cpp: In instantiation of ‘multidimensional_array<T, Dimensions>::value_type& multidimensional_array<T, Dimensions>::operator()(std::size_t, ...) [with T = int; long unsigned int ...Dimensions = {2, 3, 4, 5, 6}; multidimensional_array<T, Dimensions>::reference = int&; multidimensional_array<T, Dimensions>::value_type = int; std::size_t = long unsigned int]’:
foo.cpp:99:37: required from here
foo.cpp:60:72: error: no matching function for call to ‘multidimensional_array<int, 2, 3, 4, 5, 6>::linearise<2, 3, 4, 5, 6>(std::size_t&)’
return m_data_array[multidimensional_array::linearise<Dimensions...>(indexes)];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
foo.cpp:38:30: note: candidate: template<class> static constexpr multidimensional_array<T, Dimensions>::size_type multidimensional_array<T, Dimensions>::linearise() [with <template-parameter-2-1> = <template-parameter-1-1>; T = int; long unsigned int ...Dimensions = {2, 3, 4, 5, 6}]
static constexpr size_type linearise(void)
^~~~~~~~~
foo.cpp:38:30: note: template argument deduction/substitution failed:
foo.cpp:60:72: error: wrong number of template arguments (5, should be at least 0)
return m_data_array[multidimensional_array::linearise<Dimensions...>(indexes)];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
foo.cpp:44:30: note: candidate: template<long unsigned int First, long unsigned int ...Other> static constexpr multidimensional_array<T, Dimensions>::size_type multidimensional_array<T, Dimensions>::linearise(std::size_t, std::size_t, ...) [with long unsigned int First = First; long unsigned int ...Other = {Other ...}; T = int; long unsigned int ...Dimensions = {2, 3, 4, 5, 6}]
static constexpr size_type linearise(std::size_t index, std::size_t indexes...)
^~~~~~~~~
foo.cpp:44:30: note: template argument deduction/substitution failed:
foo.cpp:60:72: note: candidate expects 2 arguments, 1 provided
return m_data_array[multidimensional_array::linearise<Dimensions...>(indexes)];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~
Makefile:17: recipe for target 'foo.o' failed
make: *** [foo.o] Error 1
And I know why now. 我知道现在为什么。 My question is, how can I fix
linearise
so that it can pass indexes
without going through va_list
and such? 我的问题是,如何解决
linearise
,使其可以通过indexes
而无需通过va_list
等? Unfortunately linearise
is already a template, variadic function, so I can't use variadic template shenanigans in that regard. 不幸的是,
linearise
已经是模板,可变参数功能,因此在这方面,我不能使用可变参数模板。
As in preceding question the problem is that the following signatures 与前面的问题一样 ,问题在于以下签名
template<std::size_t First, std::size_t... Other>
static constexpr size_type linearise(std::size_t index,
std::size_t indexes...)
reference operator()(std::size_t indexes...)
const_reference operator()(std::size_t indexes...) const
aren't what do you mean ( indexes
a variadic list of std::size_t
) but are exactly equivalent to 不是您的意思(
indexes
std::size_t
的可变列表),但完全等同于
template<std::size_t First, std::size_t... Other>
static constexpr size_type linearise(std::size_t index,
std::size_t indexes,
...)
reference operator()(std::size_t indexes, ...)
const_reference operator()(std::size_t indexes, ...) const
where indexes
is a single std::size_t
followed by a C-style optional sequence of argument. 其中
indexes
是单个std::size_t
后跟C样式的可选参数序列。
A simple solution (you tagged C++17 but is available starting from C++11) is based on the use of variadic templates. 一个简单的解决方案(您标记了C ++ 17,但从C ++ 11开始可用)基于可变参数模板的使用。
By example, as follows 举例如下
template <std::size_t First, std::size_t ... Other, typename ... Ts>
static constexpr size_type linearise (std::size_t index,
Ts ... indexes)
{ return multidimensional_array::multiply<Other...>() * index
+ multidimensional_array::linearise<Other...>(indexes...); }
// Accessors
template <typename ... Ts>
reference operator() (Ts ... indexes)
{ return m_data_array[
multidimensional_array::linearise<Dimensions...>(indexes...)]; }
template <typename ... Ts>
const_reference operator() (Ts ... indexes) const
{ return m_data_array[
multidimensional_array::linearise<Dimensions...>(indexes...)]; }
The following is you're code, modified and compilable 以下是您的代码,已修改且可编译
#include <array>
#include <iostream>
template <typename T, std::size_t ... Dimensions>
class multidimensional_array
{
public:
using value_type = T;
using size_type = std::size_t;
private:
template <typename = void>
static constexpr size_type multiply ()
{ return 1u; }
template <std::size_t First, std::size_t ... Other>
static constexpr size_type multiply(void)
{ return First * multidimensional_array::multiply<Other...>(); }
public:
using container_type = std::array<value_type,
multidimensional_array::multiply<Dimensions...>()>;
using reference = value_type &;
using const_reference = value_type const &;
using iterator = typename container_type::iterator;
private:
container_type m_data_array;
template <typename = void>
static constexpr size_type linearise ()
{ return 0u; }
template <std::size_t First, std::size_t ... Other, typename ... Ts>
static constexpr size_type linearise (std::size_t index,
Ts ... indexes)
{ return multidimensional_array::multiply<Other...>() * index
+ multidimensional_array::linearise<Other...>(indexes...); }
public:
// Constructor
explicit multidimensional_array (const_reference value = value_type{})
{ multidimensional_array::fill(value); }
// Accessors
template <typename ... Ts>
reference operator() (Ts ... indexes)
{ return m_data_array[
multidimensional_array::linearise<Dimensions...>(indexes...)]; }
template <typename ... Ts>
const_reference operator() (Ts ... indexes) const
{ return m_data_array[
multidimensional_array::linearise<Dimensions...>(indexes...)]; }
// Iterators
iterator begin ()
{ return m_data_array.begin(); }
iterator end ()
{ return m_data_array.end(); }
// Other
void fill (const_reference value)
{ m_data_array.fill(value); }
};
int main ()
{
multidimensional_array<int, 2u, 3u, 4u, 5u, 6u> foo;
int k{ 0 };
for ( auto & s : foo )
s = k++;
std::cout << foo(0u, 0u, 0u, 1u, 0u) << std::endl;
}
Bonus suggestion. 奖金建议。
You tagged C++17 so you can use "folding". 您标记了C ++ 17,因此可以使用“折叠”。
So you can substitute the couple of multiply()
template functions 因此,您可以替换成对的
multiply()
模板函数
template <typename = void>
static constexpr size_type multiply ()
{ return 1u; }
template <std::size_t First, std::size_t ... Other>
static constexpr size_type multiply ()
{ return First * multidimensional_array::multiply<Other...>(); }
with a single folded one 与一个折叠的
template <std::size_t ... Sizes>
static constexpr size_type multiply ()
{ return ( 1U * ... * Sizes ); }
My approach is similar to that in this answer , except that instead of using std::tuple
to store a list of types , I define my own type size_t_pack
to store a (compile-time) list of size_t
's. 我的方法类似于此答案中的方法 ,除了我不使用
std::tuple
存储类型列表,而是定义了自己的类型size_t_pack
来存储size_t
的(编译时)列表。
using std::size_t;
template<size_t... values>
struct size_t_pack{};
template<size_t first_value,size_t... rest_values>
struct size_t_pack<first_value,rest_values...>{
static constexpr size_t first=first_value;
using rest=size_t_pack<rest_values...>;
static constexpr size_t product=first*rest::product;
};
template<>struct size_t_pack<>{
static constexpr size_t product=1;
};
Defines members: first
, rest
(in case not empty) and product
(since it's not possible to specialize a function using the templates of a template argument, as far as I know, another choice is to if constexpr
and make the type support checking for empty
) 定义成员:
first
, rest
(以防万一,不为空)和product
(因为据我所知,不可能使用template参数的模板来专门化一个函数,另一种选择是if constexpr
并使类型支持检查empty
With that, it's easy to define the linearize
function: 这样,很容易定义
linearize
函数:
template<class dimensions,class... SizeTs>
static constexpr size_type linearise(std::size_t index, SizeTs... indices)
{
using restDimensions=typename dimensions::rest;
return restDimensions::product *index +
multidimensional_array::linearise<restDimensions>(indices...);
}
Using a std::tuple
to store the list of types ( SizeTs
) is also possible, although struct partial specialization is still required, as far as I know. 据我所知,尽管仍然需要对结构进行部分专业化,但也可以使用
std::tuple
存储类型列表( SizeTs
)。
You need to make indexes a parameter pack by making the operator()
function a template, and expand the parameter pack when you use it by putting ...
afterwards: 你需要通过使做索引的参数包
operator()
函数模板,并展开参数包当您使用它通过把...
算账:
template <class... DimensionType>
const_reference operator()(DimensionType... indexes) const
{
return m_data_array[multidimensional_array::linearise<Dimensions...>(indexes...)];
}
See: parameter pack expansion 请参阅: 参数包扩展
The code still will not compile because of a similar problem in linearize()
, but that gets you on the right track. 由于
linearize()
中存在类似问题,因此代码仍无法编译,但这使您步入正轨。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.