使用模板在编译时初始化数组指针的 const 向量
Initializing a const vector of pointers to array at compile time using templates
以下class在C++11下无法编译;目前的循环只能在运行时执行,因此从模板 class 循环中的静态函数调用中得到 "char(*)[i] is a variably-modified type" 错误:
#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" class 处将大小为 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...
我认为这里的关键见解是你不能写:
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)();
基本上,这个想法是你有一个分配器函数的静态数组,然后索引到它。 (可能有一些聪明的方法可以使用模板来初始化地图。不过我可能会手写出来——可能使用预处理器宏)。
类似于(在 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<char(*)[]>
不可移植,因为 vector 需要一个完整的类型。
如果您定义索引容器类型特征(或者您使用 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()
方法)return 一个 char **
和 char **
的一个 bar
向量)。
下面是一个完整的编译示例
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;
}
以下class在C++11下无法编译;目前的循环只能在运行时执行,因此从模板 class 循环中的静态函数调用中得到 "char(*)[i] is a variably-modified type" 错误:
#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" class 处将大小为 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...
我认为这里的关键见解是你不能写:
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)();
基本上,这个想法是你有一个分配器函数的静态数组,然后索引到它。 (可能有一些聪明的方法可以使用模板来初始化地图。不过我可能会手写出来——可能使用预处理器宏)。
类似于(在 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<char(*)[]>
不可移植,因为 vector 需要一个完整的类型。
如果您定义索引容器类型特征(或者您使用 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()
方法)return 一个 char **
和 char **
的一个 bar
向量)。
下面是一个完整的编译示例
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;
}