我正在尝试使用整数作为类的模板参数。以下是代码示例:
template< int array_qty >
class sample_class {
public:
std::array< std::string, array_qty > sample_array;
}
如果我这样做,那就有效:
sample_class< 10 > sample_class_instance;
但是,让我说在编译时我不知道array_qty(模板参数)的值,并且只会在运行时知道它。在这种情况下,我基本上将传递一个int变量作为模板参数。为了演示,以下代码不起作用:
int test_var = 2;
int another_test_var = 5;
int test_array_qty = test_var * another_test_var;
sample_class< test_array_qty > sample_class_instance;
尝试上述操作时,在编译期间出现以下错误:
the value of ‘test_array_qty’ is not usable in a constant expression
我尝试将test_array_qty转换为const,同时将其作为模板参数传递,但这似乎也没有。有没有办法做到这一点,还是我滥用模板参数?也许他们需要在编译时知道?
目标不是要解决这个特定的方法,而是找到一种方法将数组的长度设置为一个int变量,可以在实例化类时声明。如果有办法通过模板参数执行此操作,那将是理想的。
请注意我必须使用一个数组,而不是我最终可能作为建议的矢量。此外,array_qty将始终是0到50之间的值 - 如果有所不同。
答案 0 :(得分:10)
这可以有效地完成。但是当我说你提出错误的问题时,请相信我。接下来回答你的问题,甚至认为这样做几乎总是一个坏主意。
你实际可以做的是创建50个不同的程序,每个程序对应50种可能的大小,然后有条件地跳转到你想要的程序。
template<int n>
struct prog {
void run() {
// ...
}
};
template<int n>
struct switcher {
void run(int v) {
if(v==n)
prog<n>::run();
else
switcher<n-1>::run(v);
}
};
template<>
struct switcher<-1> {
void run(int v){
}
};
调用switcher<50>::run( value );
,如果值为0到50,则调用prog<value>::run()
。在prog::run
内,模板参数是编译时值。
Horrid hack,你可能会更好地使用其他解决方案,但这正是你所要求的。
这是一个基于C ++ 14表格的版本:
template<size_t N>
using index_t = std::integral_constant<size_t, N>; // C++14
template<size_t M>
struct magic_switch_t {
template<class...Args>
using R=std::result_of_t<F(index_t<0>, Args...)>;
template<class F, class...Args>
R<Args...> operator()(F&& f, size_t i, Args&&...args)const{
if (i >= M)
throw i; // make a better way to return an error
return invoke(std::make_index_sequence<M>{}, std::forward<F>(f), i, std::forward<Args>(args)...);
}
private:
template<size_t...Is, class F, class...Args>
R<Args...> invoke(std::index_sequence<Is...>, F&&f, size_t i, Args&&...args)const {
using pF=decltype(std::addressof(f));
using call_func = R<Args...>(*)(pF pf, Args&&...args);
static const call_func table[M]={
[](pF pf, Args&&...args)->R<Args...>{
return std::forward<F>(*pf)(index_t<Is>{}, std::forward<Args>(args)...);
}...
};
return table[i](std::addressof(f), std::forward<Args>(args)...);
}
};
magic_switch_t<N>{}( f, 3, blah1, blah2, etc )
将调用f(index_t<3>{}, blah1, blah2, etc)
。
一些C ++ 14编译器会阻塞包含lambda的variardic pack扩展。这不是必需的,你可以做一个解决方法,但解决方法是丑陋的。
C ++ 14的所有功能都是可选的:你可以在C ++ 11中实现它,但又一次,丑陋。
传递的f
基本上应该是一个函数对象(lambda以auto
作为第一个参数,或者是手动的一个)。直接传递函数名称将无法正常工作,因为当第一个参数成为编译时值时,上述效果最佳。
您可以使用lambda或函数对象包装函数模板来提供帮助。
答案 1 :(得分:5)
对于C ++ 11,非类型模板参数仅限于以下内容(§14.3.2/ 1):
非类型非模板模板参数的模板参数应为以下之一:
- 表示整数或枚举类型的非类型模板参数,模板参数类型的转换常量表达式(5.19);或
- 非类型模板参数的名称;或
- 一个常量表达式(5.19),用于指定具有静态存储持续时间和外部或内部链接的对象的地址,或具有外部或内部链接的函数,包括函数模板和函数模板ID,但不包括非静态类成员,表达(忽略括号)为&amp; id-expression,除了&amp;如果名称引用函数或数组,则可以省略,如果相应的模板参数是引用,则应省略;或
- 一个求值为空指针值的常量表达式(4.10);或
- 一个常量表达式,其值为null成员指针值(4.11);或
- 指向成员的指针,如5.3.1所述。
在C ++ 98和03中,列表更受限制。底线:你不想做的事情就是不允许的。
答案 2 :(得分:3)
模板参数必须是编译时常量,也就是“常量表达式”或简称constexpr
。所以没有办法使用模板。
您可以使用动态大小的数组并将其大小存储在int
。
或者只使用vector
。一定要通过将所需的大小传递给向量的构造函数来初始化它在构造函数中的大小!
答案 3 :(得分:2)
抱歉,这是不可能的。 template参数必须是编译时已知的常量表达式。
答案 4 :(得分:0)
我来晚了,但这是我的建议。我猜测向量的主要问题是向量分配的容量比支持动态增长所需的容量大。因此,您不能编写自己的简单数组类吗?
template <typename T>
class Array {
private:
T* data;
unsigned size;
public:
Array(unsigned size) {
data = new T[size];
this->size = size;
}
T& operator[](int i) {
return data[i];
}
T operator[](int i) const {
return data[i];
}
// Depending on your needs, maybe add copy constructor and assignment operator here.
...
unsigned size() {
return size;
}
~Array() {
delete [] data;
}
}
据我所知,我认为这应该与STL数组类一样快。此外,您可以创建大小未知的数组,直到运行时才知道,销毁该数组的内存时会自动处理该数组,并且不必在每次创建大小不同的新数组时都实例化新类(例如您必须为STL阵列做)。