7

我正在尝试使用整数作为类的模板参数。这是代码示例:

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 之间的值——以防万一。

4

5 回答 5

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模板参数中是一个编译时间值。

可怕的黑客攻击,你可能会更好地使用另一种解决方案,但这是你所要求的。

这是一个基于 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 F, class...Args>
  using R=std::result_of_t<F(index_t<0>, Args...)>;
  template<class F, class...Args>
  R<F, 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<F, Args...> invoke(std::index_sequence<Is...>, F&&f, size_t i, Args&&...args)const {
    using pF=decltype(std::addressof(f));
    using call_func = R<F, Args...>(*)(pF pf, Args&&...args);
    static const call_func table[M]={
      [](pF pf, Args&&...args)->R<F, 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 的可变包扩展。这不是必需的,您可以做一个解决方法,但解决方法很丑陋。

C++14 的特性都是可选的:你可以在 C++11 中实现它,但又是丑陋的。

传递的f基本上应该是一个函数对象(一个auto作为第一个参数的 lambda,或者一个手动的)。直接传递函数名不会很好,因为当第一个参数成为编译时值时,上面的方法最有效。

您可以使用 lambda 或函数对象包装函数模板以提供帮助。

于 2013-01-19T18:58:32.723 回答
5

对于 C++ 11,非类型模板参数仅限于以下内容 (§14.3.2/1):

非类型、非模板模板参数的模板参数应为以下之一:

  • 对于整数或枚举类型的非类型模板参数,模板参数类型的转换常量表达式(5.19);或者
  • 非类型模板参数的名称;或者
  • 一个常量表达式 (5.19),它指定具有静态存储持续时间和外部或内部链接的对象或具有外部或内部链接的函数的地址,包括函数模板和函数模板 ID,但不包括非静态类成员,表示(忽略括号)作为 & id 表达式,除了如果名称引用函数或数组,则 & 可以省略,如果相应的模板参数是引用,则应省略;或者
  • 计算结果为空指针值的常量表达式(4.10);或者
  • 一个常量表达式,计算结果为空成员指针值 (4.11);或者
  • 指向成员的指针,如 5.3.1 中所述。

在 C++ 98 和 03 中,该列表受到更多限制。底线:不允许您尝试做的事情。

于 2013-01-19T18:43:18.633 回答
3

模板参数必须是编译时常量,也就是“常量表达式”或constexpr简称 s。所以没有办法使用模板。

您可以使用动态大小的数组并将其大小存储在int.

或者干脆使用vector. 请务必通过将所需大小传递给向量的构造函数来在构造函数中初始化其大小!

于 2013-01-19T18:32:33.427 回答
1

抱歉,这是不可能的。模板参数必须是编译时已知的常量表达式。

于 2013-01-19T18:34:04.173 回答
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 数组做)。

于 2018-12-12T01:03:12.733 回答