【问题标题】:Initializing a const vector of pointers to array at compile time using templates在编译时使用模板初始化指向数组的指针的 const 向量
【发布时间】:2018-04-07 19:24:03
【问题描述】:

以下类在 C++11 下无法编译;循环只能在运行时执行,因此循环内的模板类静态函数调用会出现“char(*)[i] 是可变修改类型”错误:

#include <cstddef>
#include <vector>

template <std::size_t N>
class Foo
{
private:
    const std::vector<char(*)[]> bar = bar_init();

    static std::vector<char(*)[]> bar_init()
    {
        std::vector<char(*)[]> init;

        for (size_t i = N; i > 0; i >>= 1)
        {
            auto ptr_to_array = MyClass<char(*)[i]>::static_return_ptr_to_array();
            init.emplace_back(reinterpret_cast<char(*)[]>(ptr_to_array));
        }

        return init;
    }
};

有没有一种方法可以在初始化函数中使用模板来实现相同的效果?也就是说,在“Foo”类实例化时将大小为 log2(N) 的“bar”初始化为指向 char 数组的指针的 const 向量,每个向量元素包含例如对于 N=32,输出:

MyClass<char(*)[32]>::static_return_ptr_to_array();
MyClass<char(*)[16]>::static_return_ptr_to_array();
MyClass<char(*)[8]>::static_return_ptr_to_array();

//etc...

【问题讨论】:

  • index_sequence 可能会有所帮助。
  • 你能告诉我们你的MyClass吗?还是它的简化(但可编译)版本?
  • @underscore_d 那里的问题似乎部分适用,但在我的情况下,我的向量只是 const,而不是 const static,“Foo”的每个实例化都可以使用不同的 N 值进行模板化,因此初始化过程bar 似乎需要模板专业化和运行时调用的混合。
  • @max66 我更改了我为示例发布的代码,以便更容易理解我遇到的问题。在实际代码中,模板化的静态方法调用返回一个分配器,该分配器必须在特定类型上进行模板化,在这种情况下,是某个大小的 char 数组。然后调用分配器的 allocate 方法并放弃指向 char 数组的指针,该输出就是我想放入向量中的内容。

标签: c++ c++11 templates vector metaprogramming


【解决方案1】:

类似(在 c++11 中)

template<int I> 
struct tag{};

void init( std::vector<char(*)[]>& result, tag<0> ){}

template<int I>
void init( std::vector<char(*)[]>& result, tag<I> )
{
    auto ptr_to_array = MyClass<char(*)[I]>::static_return_ptr_to_array;

    result.emplace_back(reinterpret_cast<char(*)[]>(ptr_to_array));

    init(result,tag<(I>>1)>{});
}

template <std::size_t N>
class Foo
{
private:
    const std::vector<char(*)[]> bar = bar_init();

    static std::vector<char(*)[]> bar_init()
    {
        std::vector<char(*)[]> result;

        init( result, tag<N>{} );

        return result;
    }
};

在 c++17 中,这可以通过 if constexpr 和无标签 进一步简化。此外,请注意 std::vector&lt;char(*)[]&gt; 不可移植,因为 vector 需要完整的类型。

【讨论】:

    【解决方案2】:

    我认为这里的关键见解是你不会写作:

    int i = ?? // automatic variable
    auto val = MyClass<char(*)[i]>::static_return_ptr_to_array()
    

    ... 模板参数必须是常量。

    可以做的事情是这样的:

    const std::unordered_map<int,???> allocator_map = {
        {1, MyClass<char(*)[1]>::static_return_ptr_to_array},
        {2, MyClass<char(*)[2]>::static_return_ptr_to_array},
        {4, MyClass<char(*)[4]>::static_return_ptr_to_array},
        {8, MyClass<char(*)[8]>::static_return_ptr_to_array},
        ...
    };
    

    然后

    const auto it = allocator_map.find(i);
    if (it == allocator_map.end())
        // throw error
    auto val = (it->second)();
    

    基本上,这个想法是你有一个分配器函数的静态数组,然后索引到它。 (可能有一些巧妙的方法可以使用模板来初始化地图。不过,我可能会手动将其写出来 - 可能使用预处理器宏)。

    【讨论】:

      【解决方案3】:

      如果你定义索引容器类型特征(或者你使用std::index_sequence,不幸的是只能从 C++14 开始)

      template <std::size_t ...>
      struct indexList
       { };
      

      然后你定义一个类型特征来提取一个二的递减幂的序列

      template <std::size_t, typename>
      struct iLH;
      
      template <std::size_t N, std::size_t ... Is>
      struct iLH<N, indexList<Is...>> : public iLH<(N >> 1), indexList<Is..., N>>
       { };
      
      template <std::size_t ... Is>
      struct iLH<0U, indexList<Is...>>
       { using type = indexList<Is...>; };
      
      template <std::size_t N>
      struct getIndexList : public iLH<N, indexList<>>
       { };
      
      template <std::size_t N>
      using getIndexList_t = typename getIndexList<N>::type;
      

      应该可以把你的Foo简单写成

      template <std::size_t N>
      class Foo
       {
         private:
            const std::vector<char **> bar = bar_init (getIndexList_t<N>{});
      
            template <std::size_t ... Is>
            static std::vector<char **> bar_init (indexList<Is...> const &)
             {
               std::vector<char **> init { MyClass<char(*)[Is]>::getPtr()... };
      
               return init;
             }
       };
      

      (假设MyClass 中的静态getPtr() 方法返回char **barchar ** 向量)。

      以下是完整的编译示例

      template <typename T>
      struct MyClass;
      
      template <std::size_t Dim>
      struct MyClass<char(*)[Dim]>
       {
         static char ** getPtr ()
          { static char ach[Dim]; static char * ret { ach } ; return &ret; }
       };
      
      template <std::size_t ...>
      struct indexList
       { };
      
      template <std::size_t, typename>
      struct iLH;
      
      template <std::size_t N, std::size_t ... Is>
      struct iLH<N, indexList<Is...>> : public iLH<(N >> 1), indexList<Is..., N>>
       { };
      
      template <std::size_t ... Is>
      struct iLH<0U, indexList<Is...>>
       { using type = indexList<Is...>; };
      
      template <std::size_t N>
      struct getIndexList : public iLH<N, indexList<>>
       { };
      
      template <std::size_t N>
      using getIndexList_t = typename getIndexList<N>::type;
      
      template <std::size_t N>
      class Foo
       {
         private:
            const std::vector<char **> bar = bar_init (getIndexList_t<N>{});
      
            template <std::size_t ... Is>
            static std::vector<char **> bar_init (indexList<Is...> const &)
             {
               std::vector<char **> init { MyClass<char(*)[Is]>::getPtr()... };
      
               return init;
             }
       };
      
      int main ()
       {
         Foo<32U>  f32;
       }
      

      【讨论】:

        猜你喜欢
        • 2023-01-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-11
        • 1970-01-01
        • 2015-04-28
        • 1970-01-01
        • 2012-12-19
        相关资源
        最近更新 更多