在已经具有模板变量的函数中传递可变参数 [英] Passing variadic parameters in an already template-variadic function

查看:73
本文介绍了在已经具有模板变量的函数中传递可变参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

标题不好,但我想不出更好的办法。随时更改它。



这里是我目前正在研究的模板多维数组类。我正在尝试尽可能地优化它:

  #include< array> 

模板< typename T,std :: size_t ...尺寸>
class multidimensional_array
{
public:

使用value_type = T;
使用size_type = std :: size_t;

私人:

template< typename = void>
static constexpr size_type乘法(无效)
{
return 1u;
}

template< std :: size_t首先,std :: size_t ... Other>
static constexpr size_type乘法(无效)
{
return First * multiDimension_array :: multiply< Other ...>();
}

public:

使用container_type = std :: array< value_type,multidimensional_array :: multiply< Dimensions ...>()> ;;
使用reference = value_type& ;;
使用const_reference = value_type const& ;;
使用迭代器=类型名container_type :: iterator;

私有:

container_type m_data_array;

template< typename = void>
static constexpr size_type linearise(void)
{
返回0u;
}

template< std :: size_t首先,std :: size_t ... Other>
静态constexpr size_type线性化(std :: size_t索引,std :: size_t索引...)
{
返回multiDimension_array :: multiply< Other ...>()* index + multiDimension_array :: linearise< Other ...>(索引);
}

public:

//构造函数
显式multiDimension_array(const_reference value = value_type {})
{
multiDimension_array :: fill(值);
}

//访问器
参考operator()(std :: size_t索引...)
{
return m_data_array [multiple_array :: linearise< ; Dimensions ...>(indexes)];
}

const_reference operator()(std :: size_tindex ...)const
{
return m_data_array [multiple_array :: linearise< Dimensions ...> ;(indexs)];
}

//迭代器
迭代器begin()
{
return m_data_array.begin();
}

迭代器end()
{
return m_data_array.end();
}

//其他
void fill(const_reference value)
{
m_data_array.fill(value);
}
};

我的主要功能是

  int main(void)
{
multiDimension_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;
返回0;
}

以上代码编译器无警告/错误。但是,一旦我取消注释 std :: cout 部分,我就会得到:

  g ++-7 -std = c ++ 17 -o foo.o -c foo.cpp -Wall -Wextra -pedantic 
foo.cpp:在'multidimensional_array< T,Dimensions>'的实例中:: value_type&多维数组< T,尺寸> :: operator()(std :: size_t,...)[with T = int; long unsigned int ... Dimensions = {2,3,4,5,6};多维数组< T,尺寸> :: reference = int& ;;多维数组< T,维度> :: value_type = int; std :: size_t = long unsigned int]':
foo.cpp:99:37:从此处需要
foo.cpp:60:72:错误:没有匹配的函数来调用 multiarray_array< int 、、 2、3、4、5、6> :: linearise< 2、3、4、5、6>(std :: size_t&)'
返回m_data_array [multiple_array :: linearise< Dimensions ...> ;(indexs)];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~ ^ ~~~~~~~~
foo.cpp:38:30:注意:候选人:template< class>静态constexpr多维数组< T,尺寸> :: size_type多维数组< T,尺寸> :: linearise()[具有< template-parameter-2-1> =< template-parameter-1-1> ;; T = int; long unsigned int ... Dimensions = {2,3,4,5,6}]
静态constexpr size_type linearise(void)
^ ~~~~~~~~
foo。 cpp:38:30:注意:模板参数推导/替换失败:
foo.cpp:60:72:错误:模板参数的数量错误(5,应至少为0)
return m_data_array [ multiDimension_array :: linearise< Dimensions ...>(indexes)];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~ ^ ~~~~~~~~
foo.cpp:44:30:注意:候选人:template< long unsigned int首先,long unsigned int ... Other>静态常量表达式multiDimension_array< T,Dimensions> :: size_type multiDimension_array< T,Dimensions> :: linearise(std :: size_t,std :: size_t,...)[具有长无符号int First = First; long unsigned int ... Other = {Other ...}; T = int; long unsigned int ... Dimensions = {2,3,4,5,6}]
静态constexpr size_type linearise(std :: size_t索引,std :: size_t索引...)
^〜 ~~~~~~~
foo.cpp:44:30:注意:模板参数推导/替换失败:
foo.cpp:60:72:注意:候选人期望2个参数,其中1个提供了
return m_data_array [multimedia_array :: linearise< Dimensions ...>(indexes)];
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~ ^ ~~~~~~~~
Makefile:17:目标'foo.o'的配方失败
make:*** [foo.o]错误1

我知道现在为什么。我的问题是,如何解决 linearise 以便它可以通过 indexs 而不通过 va_list 等等?不幸的是 linearise 已经是一个模板,可变参数函数,因此在这方面我不能使用可变参数模板恶作剧。

解决方案

前面的问题问题是以下签名

  template< std :: size_t首先,std :: size_t ...其他> 
静态constexpr size_type线性化(std :: size_t索引,
std :: size_t索引...)

参考运算符()(std :: size_t索引...)

const_reference运算符()(std :: size_t索引...)const

不是什么意思( indexes std :: size_t 的可变列表),但完全等效

  template< std :: size_t首先,std :: size_t ...其他> 
静态constexpr size_type线性化(std :: size_t索引,
std :: size_t索引,
...)

参考运算符()(std :: size_t索引,...)

const_reference运算符()(std :: size_t索引,...)const

其中,索引是单个 std :: size_t ,后跟C样式



一个简单的解决方案(您标记了C ++ 17,但从C ++ 11开始可用)是基于可变参数模板的使用。 / p>

例如,如下所示

  template< std :: size_t首先,std :: size_t ...其他,类型名... Ts> 
静态constexpr size_type线性化(std :: size_t索引,
Ts ...索引)
{return multiDimension_array :: multiply< Other ...>()*索引
+ multiDimension_array :: linearise< Other ...>(索引...); }

//访问器
模板< typename ... Ts>
参考operator()(Ts ...索引)
{return m_data_array [
multiDimension_array :: linearise< Dimensions ...>(indexes ...)]; }

模板< typename ... Ts>
const_reference operator()(Ts ...索引)const
{return m_data_array [
multiDimension_array :: linearise< Dimensions ...>(indexes ...)]; }

以下是您的代码,已修改且可编译

  #include< array> 
#include< iostream>

模板< typename T,std :: size_t ... Dimensions>
class multidimensional_array
{
public:
using value_type = T;
使用size_type = std :: size_t;

私人:
模板< typename = void>
static constexpr size_type乘法()
{return 1u; }

模板< std :: size_t首先,std :: size_t ... Other>
静态constexpr size_type乘法(无效)
{return First * multiDimension_array :: multiply< Other ...>(); }

public:
使用container_type = std :: array< value_type,
multiDimension_array :: multiply< Dimensions ...>()> ;;
使用reference = value_type& ;;
使用const_reference = value_type const& ;;
使用迭代器=类型名container_type :: iterator;

私人:
container_type m_data_array;

模板< typename = void>
static constexpr size_type linearise()
{return 0u; }

模板< std :: size_t首先,std :: size_t ...其他,类型名称... Ts>
静态constexpr size_type线性化(std :: size_t索引,
Ts ...索引)
{return multiDimension_array :: multiply< Other ...>()*索引
+ multiDimension_array :: linearise< Other ...>(索引...); }

public:
//构造函数
显式multiDimension_array(const_reference value = value_type {})
{multiDimension_array :: fill(value); }

//访问器
模板< typename ... Ts>
参考operator()(Ts ...索引)
{return m_data_array [
multiDimension_array :: linearise< Dimensions ...>(indexes ...)]; }

模板< typename ... Ts>
const_reference operator()(Ts ...索引)const
{return m_data_array [
multiDimension_array :: linearise< Dimensions ...>(indexes ...)]; }

//迭代器
迭代器begin()
{return m_data_array.begin(); }

迭代器end()
{return m_data_array.end(); }

//其他
无效填充(const_reference值)
{m_data_array.fill(value); }
};

int main()
{
multiDimension_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;
}

奖金建议。



您标记了C ++ 17,因此可以使用折叠。



因此,您可以替换成对的 multiply()模板函数

 模板< typename = void> 
static constexpr size_type乘法()
{return 1u; }

模板< std :: size_t首先,std :: size_t ... Other>
静态constexpr size_type乘法()
{return First * multiDimension_array :: multiply< Other ...>(); }

有一个折叠的

 模板< std :: size_t ... Sizes> 
静态constexpr size_type乘法()
{return(1U * ... * Sizes); }


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:

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? Unfortunately linearise is already a template, variadic function, so I can't use variadic template shenanigans in that regard.

解决方案

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

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.

A simple solution (you tagged C++17 but is available starting from C++11) is based on the use of variadic templates.

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".

So you can substitute the couple of multiply() template functions

  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 ); } 

这篇关于在已经具有模板变量的函数中传递可变参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆