【问题标题】:C++: Fill array according to template parameterC++:根据模板参数填充数组
【发布时间】:2011-03-10 14:30:23
【问题描述】:

基本上情况如下:

我有一个类模板(使用一个int 类型的模板参数length)并想引入一个静态数组。该数组的长度应为length,并包含元素1length

目前代码如下:

template<int length>
class myClass{
    static int array[length];
};

然后我想写一行来初始化数组

// of course, the line below does not work as intended.
template<int length> int myClass<length>::array[length]={1,2, ..., length};

(如何)实现这一点?

【问题讨论】:

标签: c++ templates arrays fill


【解决方案1】:

使用“静态构造函数”习语。

// 编辑 2

#include <iostream>

template<int length>
class myClass {
public:
    typedef int ArrayType[length];

    static struct StaticData {
        ArrayType array;

        StaticData()
        {
            for (int i = 0; i < length; i++) array[i] = i;
        }
    }
    static_data;

    static ArrayType &array;
};

template<int length>
typename myClass<length>::StaticData myClass<length>::static_data;

template<int length>
typename myClass<length>::ArrayType &myClass<length>::array = myClass<length>::static_data.array;

int main(int argc, char** argv) {
    const int LEN = 5;
    for (int i = 0; i < LEN; i++) {
        std::cout << myClass<LEN>::array[i];
    }
}

【讨论】:

  • "static constructor idiom" 给出 0 个结果 ;) 不能算是一个成语。
  • 抱歉,在 gcc-4.3 下这对我不起作用,静态构造函数永远不会被调用...除了我们需要 myClass&lt;length&gt;::StaticConstructor myClass&lt;length&gt;::static_constructor 的事实。
  • 我们可以安全地假设数组将在 static_constructor 之前初始化吗?我不确定静态数据成员是否有定义的初始化顺序(与非静态成员相反)。似乎它们应该按照它们在类定义中声明的顺序进行初始化,但是......
  • @stinky472:在同一个翻译单元中,静态变量按照它们出现的顺序进行实例化。只有在翻译单元之间才会使它们未定义。
  • 是的,这样不行。您需要确保始终引用 static_constructor。否则当用户只重复array时,它不会被填充。您的代码中还有一些关于模板使用的错误。如果您不确定,请测试您的答案。
【解决方案2】:

C 风格的数组无法做到这一点,因为它们没有值语义。

但是,如果您使用 std::tr1::array 之类的东西,那么您可以通过初始化函数结果或使用生成这些值的迭代器轻松地做您想做的事情。

【讨论】:

【解决方案3】:

您可以编写一个包装类,但我确信有更清洁的解决方案:

template <size_t length>
class array_init_1_to_n
{
    int array[length];

public:

    array_init_1_to_n()
    {
        for (int i = 0; i < length; ++i)
        {
            array[i] = i + 1;
        }
    }

    operator int*()
    {
        return array;
    }

    operator const int*() const
    {
        return array;
    }
};

template<size_t length>
class myClass{
    static array_init_1_to_n<length> array;
};

【讨论】:

    【解决方案4】:

    static constructor 中嵌入一个 for 循环,该循环运行到一定长度,它与使用初始化程序基本相同:

    for(int i = 0; i < length; i++)
        array[i] = i + 1;
    

    【讨论】:

      【解决方案5】:

      看起来很难。我能想到的最接近的方法如下:

      template<int length>
      class myClass
      {
        public:
          myClass()
          {
            static InitializeArray<length> initializeArray(&array);
          }
          template<int length>
          class InitializeArray
          {
          public:
            InitializeArray(int* array) 
            {
              for(int i = 0; i < length ; ++i)
              array[i] = i;
            }
          };
          static int array[length];
          static myClass instance;
      };
      template<int length> int myClass<length>::array[length];
      template<int length> myClass myClass::instance;
      

      【讨论】:

      • 如果不实例化 myClass 会怎样?
      • 如果没有我刚刚编辑的修改,它将无法工作。
      【解决方案6】:

      这是一个使用 Boost.MPL 的示例:

      #include <cstddef>
      #include <iostream>
      
      #include <boost/mpl/range_c.hpp>
      #include <boost/mpl/string.hpp>
      
      template<std::size_t length>
      struct myClass {
        static const std::size_t Length = length;
        typedef typename boost::mpl::c_str< boost::mpl::range_c<std::size_t, 1, length + 1> > Array;
      };
      
      int main() {
        // check whether the array really contains the indented values
        typedef myClass<10> test;
        for (std::size_t i = 0; i < test::Length; ++i) {
          std::cout << test::Array::value[i] << std::endl;
        }
      }
      

      注意数组大于length;目前它的大小是固定的。

      【讨论】:

        【解决方案7】:

        您可以使用附加静态成员的显式模板实例化,其构造函数负责填写条目:

        template<int length>
        class myClass{
        public:
            static int array[length];
        
            typedef enum{LENGTH=length} size_;
        
            struct filler
            {
                filler(void)
                {
                    for(int i=0;i<LENGTH;++i)
                        array[i]=i+1;
                }
            };
        
            static filler fill_;
        };
        
        // of course, the line[s] below now do work as intended.
        template<int length> 
        int myClass<length>::array[length];
        
        //static member definition
        template<int length>
        typename myClass<length>::filler myClass<length>::fill_;
        
        //explicit template instantiation
        template myClass<5>::filler myClass<5>::fill_;
        
        int main(void)
        {
            for(int i=0;i<myClass<5>::LENGTH;++i)
                cout<<myClass<5>::array[i]<<endl;
        
            return 0;
        }
        

        或者,由于 Benoit 已经在上面展示了类似(可能更好)的解决方案,这里有一个模板递归版本,只是为了好玩:

        //recursive version:
        template<int length>
        class myClass{
        public:
            static int array[length];
        
            typedef enum{LENGTH=length} size_;
        
            static void do_fill(int* the_array)
            {
                the_array[LENGTH-1]=LENGTH;
                myClass<length-1>::do_fill(the_array);
            }
        
            struct filler
            {
                filler(void)
                {
                    /*for(int i=0;i<LENGTH;++i)
                        array[i]=i+1;*/
                    do_fill(array);
                }
            };
        
            static filler fill_;
        };
        
        //explicit specialization to end the recursion
        template<>
        class myClass<1>{
        public:
            static int array[1];
        
            typedef enum{LENGTH=1} size_;
        
            static void do_fill(int* the_array)
            {
                the_array[LENGTH-1]=LENGTH;
            }
        };
        
        //definition of the explicitly specialized version of the array
        //to make the linker happy:
        int myClass<1>::array[1];
        
        // of course, the line below does not work as intended.
        template<int length> 
        int myClass<length>::array[length];
        
        //static member definition
        template<int length>
        typename myClass<length>::filler myClass<length>::fill_;
        
        //explicit template instantiation
        template myClass<5>::filler myClass<5>::fill_;
        
        int main(void)
        {
            for(int i=0;i<myClass<5>::LENGTH;++i)
                cout<<myClass<5>::array[i]<<endl;
        
            return 0;
        }
        

        现在,不同的编译器支持不同级别的模板递归(而且这种技术的编译器成本很高)所以,小心...“Here Be Dragons” ;-)

        哦,还有一件事,你不需要在myClass的专门版中重新定义数组,这样就可以摆脱实例化数组[1]:

        //explicit specialization to end the recursion
        template<>
        class myClass<1>{
        public:
            typedef enum{LENGTH=1} size_;
        
            static void do_fill(int* the_array)
            {
                the_array[LENGTH-1]=LENGTH;
            }
        };
        

        【讨论】:

          【解决方案8】:

          你不能将数组包装在一个静态函数中,例如,

          template<int length>
          class myClass {
              static int* myArray() {
                  static bool initd = false;
                  static int array[length];
                  if(!initd) {
                      for(int i=0; i<length; ++i) {
                          array[i] = i+1;
                      }
                      initd = true;
                  }
                  return array;
              };
          };
          

          然后像这样访问它,

          myClass<4>::myArray()[2] = 42;
          

          它将在首次使用时初始化,并且在后续访问时初始化,因为initd 是静态的,if(!initd) 将为 false,并且将跳过初始化步骤。

          【讨论】:

            【解决方案9】:

            我认为这只适用于 C++0x。在 C++03 中,无论您做什么 - 您最终都会得到一个动态初始化的数组,因此可能会出现初始化顺序问题。下面的C++0x代码就不会有这样的问题了。

            template<int...>
            struct myArray;
            
            template<int N, int ...Ns>
            struct myArray<N, Ns...> : myArray<N-1, N, Ns...> { };
            
            template<int ...Ns>
            struct myArray<0, Ns...> {
                static int array[sizeof...(Ns)];
            };
            
            template<int ...Ns>
            int myArray<0, Ns...>::array[sizeof...(Ns)] = { Ns... } ;
            
            template<int length>
            class myClass : myArray<length> {
                using myArray<length>::array;
            };
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2017-01-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-06-06
              • 2021-08-28
              相关资源
              最近更新 更多