在编译时使用模板初始化指向数组的指针的常量向量

Initializing a const vector of pointers to array at compile time using templates

本文关键字:数组 指针 向量 常量 初始化 编译      更新时间:2023-10-16

以下类在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数组的指针的常量向量,每个向量元素包含例如对于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...
类似(在

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 和 no tag<> 进一步简化。此外,请注意,std::vector<char(*)[]> 不是可移植的,因为 vector 需要完整的类型。

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

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

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

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

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

并定义一个类型特征来提取 2 的递减幂序列

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 **bar 向量 char ** )。

以下是完整的编译示例

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